2878: [Noi2012]迷失遊樂園 基環樹+DP+概率與期望

不妨用downi 表示從點i 往下走的期望步數,upi 表示從點i 往上走的期望步數。
soni 表示點i 的兒子個數,fai 表示點i 的父親個數(基環樹上的點有兩個父親)
那麼答案顯然等於

ans=ni=1downi×soni+upi×faisoni+fain

如何考慮求downisoni
直接來說基環樹上的情況,樹上的情況可以看做基環樹中環的某個點爲根的子樹的情況。
首先dfs 找環,如何從環上每個點往下做樹形DP,用w(u,v) 表示邊(u,v) 的長度。
首先自底向上DP,求出downx ,不妨令y 表示x 的兒子。
那麼有
downx=ydowny+w(x,y)sonx

這樣可以求出所有的downx
如何考慮不在環上的點,假設我們已經求出了環上點的up ,那麼自頂向下DP。
x 表示樹中的點,用f 表示父親。
upx=w(x,f)+downf×sonfdownxw(x,f)+upfsonf

那麼考慮求環上點的up ,環上的點一定是沿環的某個方向走然後進入了某一棵子樹然後走到子樹的底部。
我們不妨對點進行編號,假設從1 號點出發,沿順時針走,那麼走到二號點的概率爲1 ,走進二號店子樹的概率爲son2son2+1 然後以此類推,逆時針做一遍就可以了。
最後不要忘記將順時針和逆時針的答案加起來/2。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int N=100005;
int n,m,cnt,cir[25],head[N],next[N<<1],list[N<<1],from[N];
double key[N<<1],down[N],up[N],son[N],fa[N],pre[N];
bool vis[N];

inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}

inline void insert(int x,int y,double z)
{
    next[++cnt]=head[x];
    head[x]=cnt;
    list[cnt]=y;
    key[cnt]=z;
}

void dfs1(int x,int f)
{
    for (int i=head[x];i;i=next[i])
        if (list[i]!=f&&!vis[list[i]]) 
            dfs1(list[i],x),son[x]++;
    if (!son[x]) return;
    for (int i=head[x];i;i=next[i])
        if (list[i]!=f&&!vis[list[i]])
            down[x]+=down[list[i]]+key[i];
    down[x]/=son[x];
}

void dfs2(int x,int f)
{
    if (!son[x]) return;
    for (int i=head[x];i;i=next[i])
        if (list[i]!=f&&!vis[list[i]]) up[list[i]]+=key[i];
    if (son[x]+fa[x]>1)
        for (int i=head[x];i;i=next[i])
            if (list[i]!=f&&!vis[list[i]])
                up[list[i]]+=(down[x]*son[x]-down[list[i]]-key[i]+up[x]*fa[x])/(son[x]-1+fa[x]);
    for (int i=head[x];i;i=next[i])
        if (list[i]!=f&&!vis[list[i]]) dfs2(list[i],x);
}

void dfs_circle(int x)
{
    vis[x]=true;
    for (int i=head[x];i;i=next[i])
        if (!vis[list[i]]) from[list[i]]=x,pre[list[i]]=key[i],dfs_circle(list[i]);
        else if (list[i]!=from[x])
        {
            from[list[i]]=x; pre[list[i]]=key[i];
            for (int j=x;j!=list[i];j=from[j]) cir[++cir[0]]=j;
            cir[++cir[0]]=list[i];
            return;
        }
}

int main()
{
    n=read(); m=read();
    for (int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        double w; 
        scanf("%lf",&w);
        insert(u,v,w); insert(v,u,w);
    }
    if (m==n-1)
    {
        for (int i=1;i<=n;i++) fa[i]=1;
        memset(vis,0,sizeof(vis));
        fa[1]=0;
        dfs1(1,0);
        dfs2(1,0);
    }
    else
    {
        dfs_circle(1);
        for (int i=1;i<=n;i++) fa[i]=1;
        for (int i=1;i<=cir[0];i++) fa[cir[i]]=2;
        memset(vis,0,sizeof(vis));
        for (int i=1;i<=cir[0];i++) vis[cir[i]]=1;
        for (int i=1;i<=cir[0];i++) dfs1(cir[i],0);
//      for (int i=1;i<=cir[0];i++)
//          cout << from[cir[i]] << " "; cout << endl;
//      for (int i=1;i<=cir[0];i++)
//          cout << pre[cir[i]] << " ";
//      cout <<"!!!!!!!";   
        for (int i=1;i<=cir[0];i++)
        {
            double P;
            P=1.0;
            for (int j=1;j<cir[0];j++)
            {
                int now=cir[(i+j-1)%cir[0]+1];
//              cout << cir[i] << " " << now << " " << pre[cir[(i+j-2)%cir[0]+1]] << endl;
                if (j!=cir[0]-1) 
                    up[cir[i]]+=P*(pre[cir[(i+j-2)%cir[0]+1]]+down[now]*son[now]/(son[now]+1));
                else
                    up[cir[i]]+=P*(pre[cir[(i+j-2)%cir[0]+1]]+down[now]);
                P/=son[now]+1;
            }
//          cout << endl;
            P=1.0;
            for (int j=1;j<cir[0];j++)
            {
                int now=cir[(i-j+cir[0]-1)%cir[0]+1];
//              cout << cir[i] << " " << now << " " << pre[now] << endl;
                if (j!=cir[0]-1)
                    up[cir[i]]+=P*(pre[now]+down[now]*son[now]/(son[now]+1));
                else
                    up[cir[i]]+=P*(pre[now]+down[now]);
                P/=son[now]+1;
            }
//          cout << endl;
            up[cir[i]]/=2;
        }
        for (int i=1;i<=cir[0];i++) dfs2(cir[i],0);
    }
//  for (int i=1;i<=cir[0];i++)
//      cout << son[cir[i]] << " " ;
    double ans=0.0;
    for (int i=1;i<=n;i++)
        ans+=(down[i]*son[i]+up[i]*fa[i])/(son[i]+fa[i]);
    printf("%.5lf\n",ans/n);
    return 0;
}           
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章