[洛谷] 仓鼠找sugar II(期望DP)
Problem
题意简述:树上任意两点 \(a,b\),从 \(a\) 随机游走到 \(b\) 的期望步数。\(n \le 10^5\)。
应该是一个经典题,为什么只有洛谷上能找到。
Solution
令根为终点,注意到每个点的转移方向只有向父亲或向儿子两种,可以将期望的状态定义设计为转移向一边的期望:设 \(f[u]\) 表示 \(u->fa\) 的期望步数,即从 \(u\) 向上走一步的期望步数。则有:
(其中 \(d_u\) 表示 \(u\) 的度数)
移项,则有:
特别的,假设根为 \(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\) 发生了改变。具体的:
(其中 \(sum = \sum d_u\))
所以,\(u\) 对答案的贡献从 \(0\) 变成了 \((sum-f[v])(n-sz_v)\),而 \(v\) 对答案的贡献从 \(f[v]*sz_v\) 变成了 \(0\)。具体的:
用两遍 \(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别想半天都想不通。。。