其他分享
首页 > 其他分享> > luogu P3412 仓鼠找sugar II 期望 树形dp

luogu P3412 仓鼠找sugar II 期望 树形dp

作者:互联网

LINK:仓鼠找sugar II

以前做过类似的期望题目 加上最后的树形dp不算太难 还是可以推出来的。

容易发现 当固定起点和终点的时候 可以先固定根 这样就不用分到底是正着走还是倒着走了。

1为根 我们要求 x到y的期望步数.

由于期望的线性性 可以设出f[x]表示x到父亲的期望步数 g[x]表示父亲到儿子的期望步数。

很容易得到转移 不再赘述.

然后暴力找这条路径累加答案即可。

然后 就可以n^3的统计答案了 倍增优化一下就是n^2log的 考虑以每个点统计答案就发现可以n^2统计答案。

最后考虑树形dp 其实没必要每次统计一边答案 直接dp做。

总思想就是先统计所有x子树内所有点到x的贡献 x到自己子树内所有点的贡献。

最后是 x的子树互相跑的贡献 就可以做到O(n)了。

const int MAXN=100010;
int n,len;
int lin[MAXN],sz[MAXN],ver[MAXN<<1],nex[MAXN<<1],du[MAXN];
ll f[MAXN],g[MAXN],w[MAXN],s[MAXN],ans;
inline void add(int x,int y)
{
	ver[++len]=y;
	nex[len]=lin[x];
	lin[x]=len;++du[y];
}
inline ll ksm(ll b,ll p)
{
	ll cnt=1;
	while(p)
	{
		if(p&1)cnt=cnt*b%mod;
		b=b*b%mod;
		p=p>>1;
	}
	return cnt;
}
inline void dfs(int x,int fa)
{
	f[x]=du[x];
	go(x)if(tn!=fa)dfs(tn,x),f[x]=(f[x]+f[tn])%mod;
}
inline void dfs1(int x,int fa)
{
	go(x)
		if(tn!=fa)
		{
			g[tn]=(g[x]+f[x]-f[tn]+mod)%mod;
			dfs1(tn,x);
		}
}
inline void dp(int x,int fa)
{
	sz[x]=1;w[x]=f[x];//w[x]表示x子树内的所有点走向x的父亲的期望步数.
	s[x]=g[x];//s[x]表示x的父亲走向x子树内所有点的期望步数.
	ll cnt1=0,cnt2=0,ss=0;
	go(x)
	{
		if(tn==fa)continue;
		dp(tn,x);
		//统计子树两边互走的情况.
		ans=(ans+cnt1*sz[tn]+s[tn]*ss)%mod;
		ans=(ans+cnt2*sz[tn]+w[tn]*ss)%mod;
		//先统计所有点到x的贡献.
		ans=(ans+w[tn])%mod;
		cnt1=(cnt1+w[tn])%mod;
		w[x]=(w[x]+w[tn]+f[x]*sz[tn])%mod;
		//再统计x到所有点的贡献.
		ans=(ans+s[tn])%mod;
		s[x]=(s[x]+s[tn]+g[x]*sz[tn])%mod;
		cnt2=(cnt2+s[tn])%mod;
		ss+=sz[tn];sz[x]+=sz[tn];
	}
}
int main()
{
	freopen("1.in","r",stdin);
	get(n);
	rep(2,n,i)
	{
		int x,y;
		get(x);get(y);
		add(x,y);add(y,x);
	}
	dfs(1,0);dfs1(1,0);
	//rep(1,n,i)put(g[i]);
	dp(1,0);putl(ans*ksm((ll)n*n%mod,mod-2)%mod);
 	return 0;
}

标签:sz,P3412,int,luogu,sugar,tn,ans,dp,mod
来源: https://www.cnblogs.com/chdy/p/12779547.html