游乐园 期望+基环树dp"/>
洛谷2081 bzoj2878 NOI2012 迷失游乐园 期望+基环树dp
题目链接
题意:给你一棵无根树或基环树,求从任意一点出发,不能重复经过某个点,经过的路径的期望长度。
题解:
首先考虑树的情况。由于是无根树,我们在树形dp时通常会转为有根树。转为有根树之后我们发现,对于每个点,从它出发不能重复经过任何一个点的路径的期望长度与从它出发向子树走的期望和向父节点走的期望有关。我们设节点 x x 向子树走得到的期望长度是
分子是由父节点向其他子节点的情况(向下的总期望减去走当前 x x 的期望)加上再继续往上走的期望。
我们发现
我们算出每个点的 down[x] d o w n [ x ] 和 up[x] u p [ x ] 之后,答案就是 ∑ni=1down[i]∗son[i]+up[i]son[i]+hf[i]n ∑ i = 1 n d o w n [ i ] ∗ s o n [ i ] + u p [ i ] s o n [ i ] + h f [ i ] n
这样我们就解决了树的情况,下面考虑环的情况。
首先我们先找出哪些点在环上,然后我们把这个环看作有根树的根,这样就可以用类似的方法算出所有点的 down d o w n 。由于算 up[x] u p [ x ] 时要从根到叶子dp,所以我们要先算出环上每个点的 up u p 。我们考虑 up[x] u p [ x ] 的含义是从 x x 向父节点走的期望步数,那么对于环上的点,它们可以有顺时针和逆时针走两种选择,所以它们的父节点数是
如果再走就回到原点贡献是 概率∗(down[x]+dis[x][上一个点]) 概 率 ∗ ( d o w n [ x ] + d i s [ x ] [ 上 一 个 点 ] )
否则每个点的贡献是 概率∗(son[x]∗down[x]son[x]+1+dis[x][上一个点]) 概 率 ∗ ( s o n [ x ] ∗ d o w n [ x ] s o n [ x ] + 1 + d i s [ x ] [ 上 一 个 点 ] )
这样处理出环上的点的 up u p 之后再用类似的办法求出其他点的 up u p 。最后用同样的式子计算答案即可。
代码细节非常多,我写了不少注释。
#include <bits/stdc++.h>
using namespace std;int n,m,fa[100010],hed[100010],cnt,inq[100010],book[100010],pd,ji,sta[100010],tp;
int cir[30];//存哪些点在环上
double down[100010],up[100010],son[100010],ans;
int hf[100010];//记录每个点在有根树上父节点的个数
double gailv,len;//记录在环上走到每个点的概率和沿着环走的长度
int vis[100010];//记录环上的点是否被访问过
int fr[100010];
double dis[50][50];
struct node
{int to,next,from;double dis;
}a[500010];
void add(int from,int to,double dis)
{a[++cnt].to=to;a[cnt].dis=dis;a[cnt].from=from;a[cnt].next=hed[from];hed[from]=cnt;
}
void dfs(int x,int f)//找哪些点在环上(按照在环上的顺序记录)
{if(pd==1)return; for(int i=hed[x];i;i=a[i].next){int y=a[i].to;if(y!=f){if(inq[y]==1){pd=1;book[y]=1;cir[++ji]=y;while(sta[tp]!=y){ cir[++ji]=sta[tp];book[sta[tp]]=ji;--tp;}dis[book[x]][book[y]]=dis[book[y]][book[x]]=a[i].dis;for(int j=fr[x];j;j=fr[a[j].from]){int k=book[a[j].from],l=book[a[j].to];dis[k][l]=dis[l][k]=a[j].dis;}return;}sta[++tp]=y;inq[y]=1;fr[y]=i;dfs(y,x);inq[y]=0;--tp;fr[y]=0;} }
}
void getf(int x)//无根树转有根树
{for(int i=hed[x];i;i=a[i].next){int y=a[i].to; if(y!=fa[x]&&!book[y]){++son[x];fa[y]=x;hf[y]=1; getf(y);}}
}
void dfs1(int x)//处理down
{for(int i=hed[x];i;i=a[i].next){int y=a[i].to;if(y!=fa[x]&&son[x]&&!book[y]){dfs1(y);down[x]+=(down[y]+a[i].dis)/son[x];}}
}
void dfs2(int x)//处理up
{for(int i=hed[x];i;i=a[i].next){int y=a[i].to;if(y!=fa[x]&&(!book[y])){ if(son[x]+hf[x]-1)up[y]=a[i].dis+(son[x]*down[x]-down[y]-a[i].dis+up[x]*hf[x])/(son[x]+hf[x]-1); else if(son[x]==1)up[y]=a[i].dis+(son[x]*down[x]-down[y]-a[i].dis+up[x]);dfs2(y);}}
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=m;++i){int x,y,z;scanf("%d%d%d",&x,&y,&z);add(x,y,z);add(y,x,z);}if(m==n)//基环树{sta[++tp]=1;inq[1]=1;dfs(1,0);//找哪些点在环上for(int i=1;i<=ji;++i){getf(cir[i]);hf[cir[i]]=2;dfs1(cir[i]); } for(int i=1;i<=ji;++i) {gailv=0.5;//往环上编号大和编号小的方向走的概率都是0.5 int x=i+1,last=i;if(x>ji)x=1;while(x!=i)//往编号大的方向走{if((x%ji+1)==i)//不能走回原点up[cir[i]]+=gailv*(down[cir[x]]+dis[last][x]);elseup[cir[i]]+=gailv*(son[cir[x]]*down[cir[x]]/(son[cir[x]]+1)+dis[last][x]); gailv/=(son[cir[x]]+1);last=x;++x;if(x>ji)x=1;}gailv=0.5;x=i-1;last=i;if(x==0)x=ji;while(x!=i)//往编号小的方向走{int nxt=x;--nxt;if(nxt==0)nxt=ji;if(nxt==i)//不能走回原点up[cir[i]]+=gailv*(down[cir[x]]+dis[last][x]);elseup[cir[i]]+=gailv*(son[cir[x]]*down[cir[x]]/(son[cir[x]]+1)+dis[last][x]);gailv/=(son[cir[x]]+1);last=x;--x;if(x==0)x=ji;}}for(int i=1;i<=ji;++i)dfs2(cir[i]); for(int i=1;i<=n;++i)ans+=(down[i]*son[i]+up[i]*hf[i])/(son[i]+hf[i]);ans/=n;}else//树{getf(1);dfs1(1); dfs2(1);for(int i=1;i<=n;++i)ans+=(down[i]*son[i]+up[i])/(son[i]+hf[i]);ans/=n;}printf("%.5lf\n",ans);return 0;
}
更多推荐
洛谷2081 bzoj2878 NOI2012 迷失游乐园 期望+基环树dp
发布评论