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;
}
posted @ 2023-10-02 18:30  zifanwang  阅读(5)  评论(0编辑  收藏  举报  来源