题目描述
一棵\(n\)个节点的树,从1号结点开始游戏,在每一个点\(x\):
- 有\(a[x]/100\)的可能掉进陷阱死翘翘回到1重新开始
- 有\(b[x]/100\)的可能找到出口并结束游戏
- 剩下的可能中,你等概率随机选一条和它相连的边(可以是父亲)走过去
问期望多少步结束游戏
\(1\leq T \leq 30,1\leq n \leq 10000\)
分析
设\(dp[x]\)表示位于\(x\)节点时,期望走几步才能结束游戏
那么\(dp[x]=0.01a[x]*dp[1]+0.01b[x]*0+0.01(1-a[x]-b[x])*(\frac{1}{cnt}(\sum dp[son]+dp[1]+1))\)
看了一下数据范围发现并不能承受高斯消元的时间复杂度
我们发现,每一个点的\(dp\)值之和父亲、1号点还有儿子有关,那么叶子节点的\(dp\)值可以用父亲和1号点表示,这样往上代,合并同类项之后可以发现,每一个点的值都可以表示为:
\(dp[x]=...*dp[1]+...*dp[fa[x]]+...\)(常数项)
所以只需要维护一下每一项的系数就可以\(O(n)\)求解了,记得特判Impossible
代码
void SEARCH(int x,int pa){ f[x][0]=f[x][1]=f[x][2]=0; int cnt=0; for(int r=lst[x];r;r=nxt[r]){ cnt++; if(edge[r]==pa)continue; SEARCH(edge[r],x); } DB lef=(100-a[x]-b[x])*0.01; DB hlp=0; for(int r=lst[x];r;r=nxt[r]){ if(edge[r]==pa)continue; int pos=edge[r]; f[x][0]+=f[pos][0]; f[x][2]+=f[pos][2]; hlp+=f[pos][1]; } f[x][0]=(lef*f[x][0])/(1.0*cnt)+a[x]*0.01; f[x][2]=(lef*f[x][2])/(1.0*cnt)+lef; if(x==1)f[x][1]=0;else f[x][1]=lef/(1.0*cnt); hlp=hlp*lef/(1.0*cnt); hlp=1.0-hlp; if(x==1)hlp-=f[x][0]; if(hlp<=0)f[x][0]=f[x][1]=f[x][2]=0; else rep(i,0,2)f[x][i]/=hlp; }