2017年9月2日 机房模拟赛

来源:互联网 发布:saas数据库 编辑:程序博客网 时间:2024/05/29 13:05

显然貌似是某其他学校考过的,而我们的题竟然其他学校也在考,233,教练们是什么py(thon)交易


显然作为NOIP选手考这样一套NOIP难度的题目,考成这样确实很差,尤其是t2的思想错误,竟然认为最小树形图和最小生成树是一样的,我竟然tarjan之后在用最小生成树来做???真是羞耻啊…t1根本不会,期望以前根本没做过,现在总算是有一些起色,t3写的n方暴力,那么现在我们就来看题吧


这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述


t1是一个期望dp,我们可以从后面往前推,现状压一下,表示现在是这个集合到买满所有的东西的集合的期望(平均)次数。显然我们可以得到dp[(1<<(n-1))-1]是0,因为此时本来就已经买完了,不需要再买了。
然后现在我们来推dp转移方程

f[s]=f[s]P(s>s)+f[s]P(s>s)+1

f[s](1P(s>s))=f[s]P(s>s)+1

f[s]=(f[s]P[s>s]+1)/(1P(s>s));

解释一下上面的推导,其实已经写得非常清楚啦,就是说从所有可以转移到的新的集合转移过来,让他们的期望全部乘以概率,然后再加上自己rand到已经有的数的概率乘期望,然后自然要+1,因为我们无论是如何转移,从哪里转移,最后自己都还要走一步走到那个位置去。
然后s->s’表示转移到新集合的概率,s->s表示转移到自己的概率,然后dp是不能有环的,是一个DAG转移,所以我们移项,得到第二步的式子,最后变成第三步的式子,就可以做了,愉快地转移,看代码瞬间懂。


代码:

#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>using namespace std ;const int MAXN=2000000+10;double dp[MAXN],P[MAXN];int n,m;long long ans;int main(){    freopen("gift.in","r",stdin);    freopen("gift.out","w",stdout);    int tmp;    scanf("%d",&n);    for(register int i=1;i<=n;i++){        scanf("%lf%d",&P[i],&tmp);        if(P[i]>0) ans+=tmp;    }    m=(1<<n)-1;dp[m]=0;    for(register int s=m-1;s>=0;s--){        double sum=0;        for(int j=0;j<n;j++){            if((s&(1<<j))==0){                sum+=P[j+1];                int ss=s|(1<<j);                dp[s]+=P[j+1]*dp[ss];            }        }        dp[s]++;        dp[s]=dp[s]/sum;    }     printf("%I64d\n%.3lf\n",ans,dp[0]);    return 0;} 

t2的话,显然直接tarjan一下然后贪心就可以了,贪心策略是对于每一个scc,我们选取其入边里面权值最小的边来进行连接,因为0号节点已经是树根了,所以直接这样贪心策略完全没有问题。
代码:

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>using namespace std;const int MAXN=100000*2;int dfn[MAXN],head[MAXN],tail,low[MAXN],sta[MAXN],top,timer,scc[MAXN],cnt,fa[MAXN],N,M,ff,tt,fl;int minn[MAXN];bool vis[MAXN];struct Line{    int from,to,nxt,flow;}line[MAXN];struct Edge{    int from,to,flow;}edge[MAXN];void add_line(int from,int to,int flow){    line[++tail].from=from;    line[tail].nxt=head[from];    line[tail].to=to;    line[tail].flow=flow;    head[from]=tail;}void add_edge(int from,int to,int flow){    edge[++tail].from=from;    edge[tail].to=to;    edge[tail].flow=flow;}bool cmp(const Edge& A,const Edge& B){return A.flow<B.flow;}void tarjan(int u){    dfn[u]=++timer;    low[u]=dfn[u];    vis[u]=true;    sta[++top]=u;    for(register int i=head[u];i;i=line[i].nxt){        int v=line[i].to;        if(dfn[v]==0){            tarjan(v);low[u]=min(low[u],low[v]);        }else if(vis[v]==true){            low[u]=min(low[u],dfn[v]);        }    }    if(dfn[u]==low[u]){        scc[u]=++cnt;        vis[u]=false;        while(sta[top]!=u){            scc[sta[top]]=cnt;            vis[sta[top]]=false;            top--;        }        top--;    }}int main(){    freopen("message.in","r",stdin);    freopen("message.out","w",stdout);     while(scanf("%d%d",&N,&M)!=EOF&&(!(N==0&&M==0))){        memset(head,0,sizeof(head));tail=0;        memset(dfn,0,sizeof(dfn));cnt=0;        memset(minn,63,sizeof(minn));        for(register int i=1;i<=M;i++){            scanf("%d%d%d",&ff,&tt,&fl);ff++;tt++;            add_line(ff,tt,fl);        }        for(register int i=1;i<=N;i++){            if(dfn[i]==0) tarjan(i);        }        tail=0;        for(register int i=1;i<=M;i++){            int u=line[i].from,v=line[i].to;            if(scc[u]==scc[v]) continue;            add_edge(scc[u],scc[v],line[i].flow);        }        for(register int i=1;i<=tail;i++){            int u=edge[i].from,v=edge[i].to;            minn[v]=min(minn[v],edge[i].flow);        }        int rt=scc[1];int ass=0;        for(register int i=1;i<=cnt;i++){            if(i==rt) continue;            ass+=minn[i];        }        printf("%d\n",ass);    }    return 0;} 

t3不说了,现在都还不会,会了补上,我贴个跑了一百秒的n方大暴力


代码:

#include<bits/stdc++.h>using namespace std;const int MAXN=100000+10;struct Point{int x,y;}p[MAXN];int n,start,end,maxn[20][100000],minn[20][100000];bool cmp(const Point& A,const Point& B){return A.x<B.x;}void build(){    for(register int i=1;i<=n;i++) maxn[0][i]=minn[0][i]=p[i].y;    for(register int j=1;(1<<j)<=n;j++)        for(register int i=1;i+(1<<j)-1<=n;i++)            maxn[j][i]=max(maxn[j-1][i],maxn[j-1][i+(1<<(j-1))]),minn[j][i]=min(minn[j-1][i],minn[j-1][i+(1<<(j-1))]);}int query_max(int L,int R,int k){    return max(maxn[k][L],maxn[k][R-(1<<k)+1]);}int query_min(int L,int R,int k){    return min(minn[k][L],minn[k][R-(1<<k)+1]);}int main(){    freopen("raid.in","r",stdin);    freopen("raid.out","w",stdout);    scanf("%d",&n);    for(register int i=1;i<=n;i++){scanf("%d%d",&p[i].x,&p[i].y);}    sort(p+1,p+n+1,cmp);    build();int cnt=0;    for(register int k=2;k<=n;k++){        int kk=0;        while((1<<(kk+1))<=k) kk++;        for(register int i=1;i<=n;i++){            if(i+k-1>n) break;            int maks=query_max(i,i+k-1,kk),miks=query_min(i,i+k-1,kk);             if(maks==miks+k-1) cnt++;        }    }    printf("%d\n",cnt+n);    return 0;}