P9454 [ZSHOI-R1] 巡城 题解
因为任何两个城市之间的路径至多只有一条不经过 \(1\) 号节点,所以删掉 \(1\) 号节点后,剩下的图成一个森林。题意就是求从点 \(1\) 开始 \(\tt dfs\),每个点的 \(\tt dfs\) 序的期望。
先考虑每个树内部的贡献。我们以这个树内第一个被搜到的节点为根,对于树内的任意一个节点,它的所有祖先对它的贡献是 \(1\),所有子孙对它没有贡献,其它节点对它的贡献是 \(\frac{1}{2}\)(在 \(\tt lca\) 处有两个子树各有一半概率被先搜到)。
可以树形 \(\tt dp\),分别求出 \(E(\text{祖先个数})\) 和 \(E(\text{子树大小})\),树内的贡献就是 \(\frac{\text{树的大小}+E(\text{祖先个数})-E(\text{子树大小})}{2}\)。实现方法比较容易就不多讲了。
再考虑不同树之间的贡献。对于两棵树 \(S\) 和 \(T\),我们记它们与点 \(1\) 有一条边直接连接的点的个数分别是 \(c_S\) 和 \(c_T\)。只考虑这两棵树的遍历顺序,很容易可以得到 \(S\) 对 \(T\) 中每个点的贡献是 \(\frac{c_S|S|}{c_S+c_T}\),于是就有了一种 \(O(n^2)\) 的做法。
考虑怎么优化,发现 \(c_S\) 最多只有 \(\sqrt n\) 种不同的值,可以将这些树分在一组,统一处理,最后减去自己对自己的贡献,两两暴力计算的时间复杂度是 \(O(n)\)。
总时间复杂度 \(O(n+m)\)。
代码:
#include<bits/stdc++.h>
#define ll long long
#define mxn 500003
#define md 998244353
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
using namespace std;
int power(int x,int y){
int ans=1;
for(;y;y>>=1){
if(y&1)ans=(ll)ans*x%md;
x=(ll)x*x%md;
}
return ans;
}
int n,m,c,c1,a[mxn],d[mxn],f[mxn],s[mxn],sm[mxn],sz[mxn],up[mxn],as[mxn],ans[mxn];
vector<int>e,g[mxn];
bool v[mxn],b[mxn];
ll xi;
void dfs(int x){
v[x]=1,c++,sz[x]=1;
if(b[x])c1++;
for(int i:g[x])if(!v[i]){
dfs(i);
s[x]+=s[i];
sz[x]+=sz[i];
}
}
void dfs1(int x,int fa){
f[x]=s[x]-b[x];
ans[x]=c+1;
for(int i:g[x])if(i!=fa&&i!=1){
dfs1(i,x);
f[x]=(f[x]+f[i])%md;
}
}
void dfs2(int x,int fa){
a[x]=c,d[x]=c1;
if(fa)up[x]=(up[fa]+(c1-s[x])+f[fa]-f[x]-s[x])%md;
ans[x]=(ans[x]+(up[x]+f[x])*xi)%md;
ans[x]=(ans[x]-(c-sz[x])*xi%md*s[x])%md;
for(int i:g[x])if(i!=fa&&i!=1)ans[x]=(ans[x]-sz[i]*xi%md*(c1-s[i]))%md;
for(int i:g[x])if(i!=fa&&i!=1){
dfs2(i,x);
}
}
signed main(){
scanf("%d%d",&n,&m);
for(int i=0,x,y;i<m;++i){
scanf("%d%d",&x,&y);
g[x].pb(y),g[y].pb(x);
if(x==1)b[y]=1,s[y]++;
if(y==1)b[x]=1,s[x]++;
}
v[1]=1;
for(int i:g[1])if(!v[i]){
c=0,c1=0;
dfs(i);
xi=power(c1,md-2);
dfs1(i,0);dfs2(i,0);
sm[c1]=(sm[c1]+c)%md;
}
rep(i,1,n)if(sm[i])e.pb(i);
for(int i:e){
for(int j:e)if(i!=j)as[i]=(as[i]+(ll)sm[j]*j%md*power(i+j,md-2))%md;
}
rep(i,1,n){
ans[i]=(ans[i]*499122177ll%md+md)%md;
ans[i]=(ans[i]+as[d[i]]+(sm[d[i]]-a[i])*499122177ll%md)%md;
}
rep(i,1,n)printf("%d ",(ans[i]+1)%md);
return 0;
}