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
",ans);
return 0;
}
/*
3
1 2
1 3
110916041
*/
Summary
经典题,没啥好说的。
-
“将期望的状态定义设计为转移向一边的期望”,具体一点,可以描述为 “(i -> i+1) 的期望步数”
-
换根dp别想半天都想不通。。。