【BZOJ】2878: [Noi2012]迷失游乐园-基环树期望DP

版权声明:欢迎转载(请附带原链接)ヾ(๑╹◡╹)ノ" https://blog.csdn.net/corsica6/article/details/84428811

传送门:bzoj2878


题解

讨论比较繁琐:
先求出每个点向下走的期望 g i g_i ,再求出向上走的期望 f i f_i 。设 i i 的儿子集合为 s o n i son_i d i s i , j dis_{i,j} 表示 i , j i,j 之间的距离, f a i fa_i 表示 i i 的父亲结点, d i d_i 表示 i i 的度数。

树:
g i = 1 s o n i j s o n i ( g j + d i s i , j ) g_i=\dfrac{1}{|son_i|}\sum\limits_{j\in son_i}(g_j+dis_{i,j})

f i = ( g f i × s o n f i g i d i s f a i , i + f f i d f a i 1 ) + d i s f a i , i f_i=(\dfrac{g_{f_i}\times|son_{f_i}|-g_i-dis_{fa_i,i}+f_{f_i}}{d_{fa_i}-1})+dis_{fa_i,i}

每个点的期望: g i × s o n i + f i d i \dfrac{g_i\times|son_i|+f_i}{d_i}

基环树:

设缩点后的环为根,先不考虑环边求出所有点的 g g 值。

因为环点 20 \leq 20 ,考虑分别 n 2 n^2 枚举每个点顺时针和逆时针向上走的期望,概率都为 0.5 0.5

设起点为 x x ,下一个点为 t o x to_x ,则:
f x = d t o x 2 d t o x 1 × g t o x + 1 d t o x 1 f t o x + d i s f x , x f_x=\dfrac{d_{to_x}-2}{d_{to_x}-1}\times g_{to_x}+\dfrac{1}{d_{to_x}-1}f_{to_x}+dis_{f_x,x}

因为在环上,所以如果一直沿着环走迭代到 x x 前一个点 f r x fr_x 时, f f r x = 0 f_{fr_x}=0 ,倒着退回去就可以了。

求出环上的 f i f_i 之后再带回子树即可。

p.s. 注意讨论分母为0的情况。


代码

#include<bits/stdc++.h>
using namespace std;
typedef double db;
const int N=1e5+100;

int n,m,d[N];
int head[N],to[N<<1],nxt[N<<1],w[N<<1],tot;
db f[N],g[N],ans;

char cp;
inline void rd(int &x)
{
	cp=getchar();x=0;
	for(;!isdigit(cp);cp=getchar());
	for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
}

inline void lk(int u,int v,int cc)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=cc;}

namespace tree{
	
	void gtdn(int x,int fr)
	{
		int sn=(d[x]-(x!=1));if(!sn) return;
		for(int j,i=head[x];i;i=nxt[i]){
			j=to[i];if(j==fr) continue;
			gtdn(j,x);g[x]+=(g[j]+w[i]);
		}
		g[x]/=(db)sn;
	}
	
	void gtup(int x,int fr)
	{
		int sn=d[x]-(x!=1),j,i;db bs=f[x]+g[x]*sn;
		for(i=head[x];i;i=nxt[i]){
			j=to[i];if(j==fr) continue;
			f[j]=(db)w[i];
			if(d[x]>1) f[j]+=(bs-g[j]-w[i])/(db)(d[x]-1);
			gtup(j,x);
		}
	}
	
    inline void sol()
	{
		int i,x,y,z;
		for(i=1;i<n;++i){
		   rd(x);rd(y);rd(z);d[x]++;d[y]++;
		   lk(x,y,z);lk(y,x,z);
		}
		gtdn(1,0);gtup(1,0);
		for(i=1;i<=n;++i) ans+=((d[i]-(i!=1))*g[i]+f[i])/(db)d[i];
		ans/=(db)n;
    }
    
}

namespace Basc{
	#define trs(x) (x>cnt)?(x-cnt):x
	db sup[N],nup[N];
	int st,ed,lca,dep[N],fa[N],fr[N],dir[N];
	int h[N],cnt,len[22][22];
	bool cir[N];

    int getfa(int x){return x==fa[x]?x:fa[x]=getfa(fa[x]);}
	
	void dfs(int x)
	{
		for(int j,i=head[x];i;i=nxt[i]){
			j=to[i];if(j==fr[x]) continue;
			fr[j]=x;dep[j]=dep[x]+1;dfs(j);
		}
	}
	
	void gtdn(int x)
	{
		int sn=(d[x]-1-(cir[x]?1:0));if(!sn) return;
		for(int j,i=head[x];i;i=nxt[i]){
			j=to[i];if(j==fr[x] || cir[j]) continue;
			fr[j]=x;gtdn(j);g[x]+=(g[j]+w[i]);
		}
		g[x]/=(db)sn;
	}
	
	void gtup(int x)
	{
		int sn,j,i;db bs;
		if(cir[x]){sn=d[x]-2;bs=f[x]*2+g[x]*sn;}
		else{sn=d[x]-1;bs=f[x]+g[x]*sn;}
		if(!sn) return;
		for(i=head[x];i;i=nxt[i]){
			j=to[i];if(j==fr[x] || cir[j]) continue;
			f[j]=w[i]+(bs-g[j]-w[i])/(db)(d[x]-1);
			gtup(j);
		}
	}
	
    inline void sol()
	{
		int i,j,k,x,y,z,ix,iy,nt,vl;
    	for(i=1;i<=n;++i) fa[i]=i;
    	for(i=1;i<=n;++i){
    		rd(x);rd(y);rd(z);d[x]++;d[y]++;
    		ix=getfa(x);iy=getfa(y);
    		if(ix==iy) {st=x;ed=y;vl=z;continue;}
    		fa[ix]=iy;lk(x,y,z);lk(y,x,z);
    	}
    	dep[1]=1;dfs(1);cir[st]=cir[ed]=true;
    	lk(st,ed,vl);lk(ed,st,vl);
    	for(x=st,y=ed;x!=y;x=fr[x]){
    		if(dep[x]<dep[y]) swap(x,y);
    		dir[fr[x]]=x;cir[fr[x]]=true;
    	}
    	lca=x;
    	for(x=st;;x=fr[x]) {h[++cnt]=x;if(x==lca) break;}
    	if(ed!=lca){
    		for(y=ed;;y=fr[y]) if(fr[y]==lca) break;
    		for(;y;y=dir[y]) h[++cnt]=y;
    	}
    	for(i=1;i<=cnt;++i){
    		h[cnt+i]=h[i];fr[h[i]]=0;gtdn(h[i]);
    	    nt=(i==cnt)?1:(i+1);
    	    for(j=head[h[i]];(to[j]!=h[nt]);j=nxt[j]);
    	    len[i][nt]=len[nt][i]=w[j];
		}
		
    	for(i=1;i<=cnt;++i){
    		sup[cnt+i-2]=len[trs(cnt+i-2)][trs(cnt+i-1)]+g[h[cnt+i-1]];
    		for(j=cnt+i-3;j>=i;--j){
    			sup[j]=len[trs(j)][trs(j+1)];
    			sup[j]+=((d[h[j+1]]-2)*g[h[j+1]]+sup[j+1])/(db)(d[h[j+1]]-1);
    		}
    		f[h[i]]=sup[i];
    	}
    	for(i=cnt+1;i<=cnt+cnt;++i){
    		sup[i-cnt+2]=len[trs(i-cnt+1)][trs(i-cnt+2)]+g[h[i-cnt+1]];
    		for(j=i-cnt+3;j<=i;++j){
    			sup[j]=len[trs(j-1)][trs(j)];
    			sup[j]+=((d[h[j-1]]-2)*g[h[j-1]]+sup[j-1])/(db)(d[h[j-1]]-1);
    		}
    		f[h[i]]+=sup[i];f[h[i]]*=0.5;
    	}
    	for(i=1;i<=cnt;++i) gtup(h[i]);	
    	
    	for(i=1;i<=n;++i){
    		if(cir[i]) ans+=(2*f[i]+(d[i]-2)*g[i])/(db)d[i];//(d[x]-2)->(d[i]-2)
    		else ans+=(f[i]+(d[i]-1)*g[i])/(db)d[i];
    	}
    	ans/=(db)n;
    }
}

int main(){
	rd(n);rd(m);
	if(m==n-1) tree::sol();
	else Basc::sol();
	printf("%.5lf",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/84428811