Luogu P3412 仓鼠找sugar II
给定一棵 \(n\) 个节点的树,求任意两点 \(u\) 和 \(v\) 间随机游走步数的期望。
\(n \leq 100000\)。
考虑先固定 \(v\),此时不妨令 \(v\) 为树根。
此时我们求出 \(x_u\) 表示 \(u\) 走到 \(fa_u\) 的期望步数,则答案为 \(\sum\limits_{u=1}^{n} size_u \times x_u\)。
考虑如何求出 \(x_u\)。令 \(u\) 的孩子个数为 \(t\),容易列出 \(x_u = \dfrac{1 + \sum\limits_{p \in son_u}(1 + x_p + x_u)}{t+1}\)。
此时我们容易写出 \((t+1)x_u = 1 + t \times x_u + \sum\limits_{p \in son_u} (x_p + 1)\),即 \(x_u = 1 + \sum\limits_{p \in son_u} (x_p + 1)\)。
接下来你就获得了 \(O(n^2)\) 的做法,考虑优化。
本质上,我们只需要求出 \(u\) 到其所有相邻点的期望步数,这一点是容易在树上得到的。
接下来考虑一条有向边 \((u,v)\) 的贡献,发现其产生贡献当且仅当 \(u\) 到 \(v\) 经过 \((u,v)\),只需要求出两边大小乘积即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const long long mod=998244353;
int child[100010];
long long x_1[100010],x_2[100010];
vector<int> G[100010];
void dfs_1(int u,int fa){
child[u]=1;
x_1[u]=1;
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v!=fa){
dfs_1(v,u);
child[u]+=child[v];
x_1[u]+=x_1[v]+1;
}
}
}
void dfs_2(int u,int fa){
long long pre=0;
if(fa!=-1){
pre+=x_2[u]+1;
}
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v!=fa){
x_2[v]=1;
pre+=x_1[v]+1;
}
}
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v!=fa){
x_2[v]+=pre-(x_1[v]+1);
dfs_2(v,u);
}
}
}
long long inv(long long num){
long long pre=mod-2,ans=1;
while(pre){
if(pre&1){
ans=ans*num%mod;
}
num=num*num%mod;
pre>>=1;
}
return ans;
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v;
scanf("%d %d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs_1(1,-1);
dfs_2(1,-1);
long long ans=0;
for(int i=2;i<=n;i++){
ans+=(x_1[i]+x_2[i])%mod*child[i]%mod*(n-child[i])%mod;
ans%=mod;
}
printf("%lld",ans*inv((long long)n*n%mod)%mod);
return 0;
}

浙公网安备 33010602011771号