[洛谷] 仓鼠找sugar II(期望DP)

Problem

题目地址

题意简述:树上任意两点 \(a,b\),从 \(a\) 随机游走到 \(b\) 的期望步数。\(n \le 10^5\)

应该是一个经典题,为什么只有洛谷上能找到。

Solution

终点,注意到每个点的转移方向只有向父亲或向儿子两种,可以将期望的状态定义设计为转移向一边的期望\(f[u]\) 表示 \(u->fa\) 的期望步数,即从 \(u\) 向上走一步的期望步数。则有:

\[f[u] = \frac{1}{d_u} + \frac{\sum (f[v]+f[u]+1)}{d_u} \]

(其中 \(d_u\) 表示 \(u\) 的度数)

移项,则有:

\[f[u] = d_u + \sum f[v] \]

特别的,假设根为 \(x\),有 \(f[x]=0\)

\(g(x)\)表示根为\(x\)(即终点为 \(x\))时的答案,\(dep'[u]\) 表示 \(u->x\) 的期望步数。根据定义:\(g(x)=\sum dep'[u]\),其中:\(dep'[u]=dep'[fa]+f[u]\)。进一步我们可以发现,\(g(x)=\sum f[u]*sz_u\) 其中 \(sz_u\) 表示 \(u\) 的子树大小。

由于\(x\) 可以是 \(1 \sim n\) 的任意一个点,所以我们需要算出 \(g(x),1 \le x \le n\)。于是我们可以写一个换根dp

假设 \(u\) 是当前的根,\(v\)\(u\) 的儿子且是下一个根。可以发现根从 \(u\) 换到 \(v\)\(f[\ ]\) 数组中仅有 \(f[u]\)\(f[v]\) 发生了改变,同样的,只有 \(sz_u\)\(sz_v\) 发生了改变。具体的

\[f[u] = sum-f[v],\ f[v]=0 \]

(其中 \(sum = \sum d_u\)

所以,\(u\) 对答案的贡献从 \(0\) 变成了 \((sum-f[v])(n-sz_v)\),而 \(v\) 对答案的贡献从 \(f[v]*sz_v\) 变成了 \(0\)具体的

\[g(v)=g(u)+(sum-f[v])(n-sz_v)-f[v]*sz_v \]

用两遍 \(Dfs\) 即可实现,时间复杂度 \(O(n)\)

Code

Talk is cheap.Show me the code.

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
    return x * f;
}
const int N = 100007, mod = 998244353;
int n,cnt,sum;
int f[N],g[N],sz[N],deg[N];
int head[N];
struct Edge {
	int next,to;
}edge[N<<1];
inline void add(int u,int v) {
	edge[++cnt] = (Edge)<%head[u],v%>;
	head[u] = cnt;
}
inline void Add(int &x,int y) {
	x = (x+y>mod ? x+y-mod : x+y);
}
inline void Dec(int &x,int y) {
	x = (x-y<0 ? x-y+mod : x-y);
}
void Dfs1(int u,int fa) {
	sz[u] = 1; f[u] = deg[u];
	for(int i=head[u];i;i=edge[i].next) {
		int v = edge[i].to;
		if(v != fa) {
			Dfs1(v,u); sz[u] += sz[v]; f[u] += f[v];
		}
	}
}
void Dfs2(int u,int fa) {
	for(int i=head[u];i;i=edge[i].next) {
		int v = edge[i].to;
		if(v != fa) {
			Add(g[v], g[u]); Dec(g[v], f[v]*sz[v]%mod); Add(g[v], (sum-f[v])*(n-sz[v])%mod);
			Dfs2(v,u);
		}
	}
}
int Pow(int x,int y) {
	int res = 1, base = x;
	while(y) {
		if(y&1) res = res*base%mod; base = base*base%mod; y >>= 1;
	}
	return res;
}
signed main()
{
	n = read();
	for(int i=1;i<n;++i) {
		int u = read(), v = read();
		add(u,v), add(v,u); ++deg[u], ++deg[v];
	}
	Dfs1(1,0);
	f[1] = 0;
	for(int i=1;i<=n;++i) Add(g[1], f[i]*sz[i]%mod);
	for(int i=1;i<=n;++i) sum += deg[i];
	Dfs2(1,0);
	int ans = 0;
	for(int i=1;i<=n;++i) Add(ans, g[i]);
	ans = ans*Pow(n*n%mod, mod-2)%mod;
	printf("%lld\n",ans);
	return 0;
}
/*
3
1 2
1 3

110916041
*/

Summary

经典题,没啥好说的。

  • 将期望的状态定义设计为转移向一边的期望”,具体一点,可以描述为 “\(i\ ->\ i+1\) 的期望步数

  • 换根dp别想半天都想不通。。。

posted @ 2020-11-18 18:56  基地AI  阅读(94)  评论(0编辑  收藏  举报