首页 > 代码库 > BZOJ 2878: [Noi2012]迷失游乐园

BZOJ 2878: [Noi2012]迷失游乐园

题目链接

题意:

给你一张n个点n-1或n条边的带权无向图。从每个点出发一直走下去,不能重复经过某个点。问走过的路径长度的数学期望是多少?

N<=100000。图中至多只有一个环,并且环长不超过20。

题解:

首先考虑是一颗树的情况:

首先随便取一个点为根(例如1号点),做树状动规。

设点i走向它的子树的期望长度为F[i],i的子节点为s1、s2……sk。

设D[i] = ∑( F[sj] + edge(i,sj) ) ,那么F[i] = D[i] / k。

设P[i]为i作为起点的期望长度,那么P[1]=F[1],Du[i]是节点i的度数。

再进行一次DFS,如果y是x的子节点,P[x]已经算出。

设节点y向x走对期望的贡献是other,other=(P[x]*Du[x]-edge(x,y)-F[x])/(Du[x]-1)+edge(x,y)

P[y]=(other+D[y])/Du[y]

这样通过两次DFS,就可以算出所有点的P值。

但是这个题可能是一颗基环树。

首先可以通过dfs找出这个环,如果去掉这个环,那就是一些子树构成的森林。

首先用前面提到的树形动规的方法处理这些子树,求出D和F。

然后注意到环上的点很少,我们就依次让环上的每个点作为起点计算一遍。

对于环上的点x,设calc_pre(x)为从x向环上左侧走得到的期望长度,calc_nxt(x)为从x向环上右侧走得到的期望长度。

sx为x的孩子。

那么P[x]=(∑(F[sx]+edge(x,sx)+calc_pre(x)+calc_nxt(x))/Du(x)。

其中calc_pre,calc_nxt可以O(环上点个数)复杂度求出。

于是就可以再次dfs算出所有点的P。

ans=(∑P[i])/n

/**************************************************************    Problem: 2878    User: zrts    Language: C++    Result: Accepted    Time:588 ms    Memory:10748 kb****************************************************************/ #include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<vector>//by zrt//problem:using namespace std;typedef long long LL;const int inf(0x3f3f3f3f);const double eps(1e-9);bool oncircle[100005];int du[100005];int fa[100005];int H[100005],X[200005],P[200005],tot;double E[200005];inline void add(int x,int y,int z){//add an edge    P[++tot]=y;X[tot]=H[x];H[x]=tot;E[tot]=z;}int n,m;double F[100005];//for son Edouble D[100005];// F * |son|double p[100005];// ansint num[100005];// |son|int pre[100005];double wpre[100005];bool type;void treedp1(int x){//Dp the tree first    for(int i=H[x];i;i=X[i]){        if(P[i]==fa[x]||oncircle[P[i]]) continue;        fa[P[i]]=x;        treedp1(P[i]);        D[x]+=F[P[i]]+E[i];num[x]++;    }    if(num[x])    F[x]=D[x]/num[x];    else F[x]=0;}void treedp2(int x,double edge){//Dp the tree second | calc the P    double other;    if(!fa[fa[x]]) {        if(!type){            if(num[fa[x]]>1) other=(p[fa[x]]*(num[fa[x]]+0)-edge-F[x])/(num[fa[x]]-1)+edge;            else other=edge;        }else{            other=(p[fa[x]]*(num[fa[x]]+2)-edge-F[x])/(num[fa[x]]+1)+edge;        }    }    else other=(p[fa[x]]*(num[fa[x]]+1)-edge-F[x])/num[fa[x]]+edge;         p[x]=(other+D[x])/(num[x]+1);    for(int i=H[x];i;i=X[i]){        if(P[i]==fa[x]||oncircle[P[i]]) continue;        treedp2(P[i],E[i]);    }}vector<int> circle;bool ok;int nxt[100005];double wnxt[100005];void findcircle(int x,int fa){    for(int i=H[x];i;i=X[i]){        if(P[i]==fa) continue;        if(pre[P[i]]){            ok=1;            int k=x;            pre[P[i]]=x;            wpre[P[i]]=E[i];            do{                oncircle[k]=1;                circle.push_back(k);                k=pre[k];            }while(k!=P[i]);            oncircle[k]=1;            circle.push_back(k);        }else{            pre[P[i]]=x;wpre[P[i]]=E[i];            findcircle(P[i],x);            if(ok) return;        }    }}double calc_nxt(int now,int end){    double ret=wnxt[now];    now=nxt[now];    int extra=0;    if(nxt[now]!=end) ret+=calc_nxt(now,end)/(du[now]-1);    else extra=-1;    for(int i=H[now];i;i=X[i]){        if(oncircle[P[i]]) continue;        ret+=(E[i]+F[P[i]])/(du[now]-1+extra);    }    return ret;}double calc_pre(int now,int end){    double ret=wpre[now];    now=pre[now];    int extra=0;    if(pre[now]!=end) ret+=calc_pre(now,end)/(du[now]-1);    else extra=-1;    for(int i=H[now];i;i=X[i]){        if(oncircle[P[i]]) continue;        ret+=(E[i]+F[P[i]])/(du[now]-1+extra);    }    return ret;}int main(){// the main function    #ifdef LOCAL    freopen("in.txt","r",stdin);    freopen("out.txt","w",stdout);    #endif    scanf("%d%d",&n,&m);    for(int i=0,x,y,z;i<m;i++){        scanf("%d%d%d",&x,&y,&z);        du[x]++;        du[y]++;        add(x,y,z);        add(y,x,z);    }    if(m==n-1){        treedp1(1);        p[1]=F[1];        for(int i=H[1];i;i=X[i]){            treedp2(P[i],E[i]);        }        double ans=0;        for(int i=1;i<=n;i++){             ans+=p[i];        }        printf("%.5f\n",ans/n);    }else{        type=1;        pre[1]=-1;        findcircle(1,-1);        for(int now=0;now<circle.size();now++){            nxt[pre[circle[now]]]=circle[now];            wnxt[pre[circle[now]]]=wpre[circle[now]];        }        for(int now=0;now<circle.size();now++){            for(int i=H[circle[now]];i;i=X[i]){                if(oncircle[P[i]]) continue;                treedp1(P[i]);                p[circle[now]]+=(F[P[i]]+E[i])/du[circle[now]];                num[circle[now]]++;            }        }        for(int now=0;now<circle.size();now++){            p[circle[now]]+=(calc_pre(circle[now],circle[now])+calc_nxt(circle[now],circle[now]))/du[circle[now]];        }        for(int now=0;now<circle.size();now++){            for(int i=H[circle[now]];i;i=X[i]){                if(oncircle[P[i]]) continue;                fa[P[i]]=circle[now];                treedp2(P[i],E[i]);            }        }        double ans=0;        for(int i=1;i<=n;i++){            ans+=p[i];        }        printf("%.5f\n",ans/n);    }    return 0;}

BZOJ 2878: [Noi2012]迷失游乐园