题解:P9454 [ZSHOI-R1] 巡城
题面:
从 \(1\) 出发,求期望 dfn 序。
\(1\) 点很特殊,先固定 \(1\) 点,发现去除 \(1\) 后是森林,而 \(1\) 把他们都连起来了。
由于 \(1\) 开始走,所以相当于 \(1\) 连的边钦定了很多树的很多根。
先固定一棵树的一个根 \(rt\),思考这时 \(x\) 点的期望 dfn 序会被三种点贡献:
- \(rt→x\) 每个点贡献 \(1\)。
- \(x\) 子树内每个点贡献 \(0\)。
- 其他节点贡献 \(\frac{1}{2}\)。
有点反直觉证一下:
假设 \(v\) 为其他节点,\(x\) 与 \(v\) 相对顺序只有两种,手玩一下就知道了。
根之间是平等的,所以把所有根跑一遍所有节点的期望 dfn 序,每个节点可以把加和除以当前树根的总数。
\(dfn_x=\frac{\sum_{rt}dis(x,rt)+siz_{rt}-(siz_x-1)+1}{2×cnt}\)
(这里的 \(cnt\) 指 \(x\) 所在树树根个数)
可以换根 DP。
此时是不考虑树之间影响时每个节点的期望 dfn 序。
考虑树之间影响怎么办?这时可以看出来我们进一个树就必须跑完这个树,从树遍历的相对顺序来看,显然 \(T\) 对 \(S\) 造成贡献是 \(\frac{cnt_Tsiz_T}{cnt_S+cnt_T}\)(这里的 \(cnt_T\) 指 \(T\) 树根个数)。这题数据很水直接枚举树对就能过,但是实际上我们还有最后一步:
把 \(cnt_T\) 相同的树合并成一块,合并完显然最多 \(O(\sqrt n)\) 种,在两两块之间计算贡献,可以发现只需要对每个块记录 \(\sum siz\) 就能做到。
按上面模拟就好了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int QAQ=5e5+7,mo=998244353;
int n,m,gj[QAQ];
vector<int> dian[QAQ];
int siz[QAQ],cntrt[QAQ],dis[QAQ];
int f[QAQ];
int find(int x) {return x==f[x]?f[x]:f[x]=find(f[x]);}
void dfs1(int x,int ba)
{
cntrt[x]=gj[x];
siz[x]=1;
dis[x]=0;
for(int v:dian[x])
{
if(v==ba||v==1) continue;
dfs1(v,x);
f[find(v)]=find(x);
dis[x]+=dis[v]+cntrt[v];
siz[x]+=siz[v];
cntrt[x]+=cntrt[v];
}
}
int ksm(int x,int k)
{
int da=1;
for(;k;k>>=1,x=x*x%mo) if(k&1) da=da*x%mo;
return da;
}
int ans[QAQ],g[QAQ];
void dfs2(int x,int ba)
{
ans[x]=dis[x]+g[x]+
siz[find(x)]*cntrt[find(x)]
-(cntrt[find(x)]-cntrt[x])*(siz[x]-1)
-gj[x]*(siz[find(x)]-1)+
cntrt[find(x)];
ans[x]%=mo;
for(int v:dian[x])
{
if(v==ba||v==1) continue;
ans[x]-=cntrt[v]*(siz[find(x)]-siz[v]-1);
ans[x]%=mo;
}
ans[x]=ans[x]*ksm(2,mo-2)%mo*ksm(cntrt[find(x)],mo-2)%mo;
for(int v:dian[x])
{
if(v==ba||v==1) continue;
g[v]=g[x]+cntrt[find(x)]-cntrt[v]+dis[x]-(dis[v]+cntrt[v]);
dfs2(v,x);
}
}
vector<int> cun,ccx;
int cf[QAQ],tong[QAQ];
signed main()
{
cin>>n>>m;
for(int i=1,u,v;i<=m;i++)
cin>>u>>v,dian[u].push_back(v),dian[v].push_back(u);
for(int i=1;i<=n;i++) f[i]=i;
for(int v:dian[1]) gj[v]=1;
for(int v:dian[1]) if(v==find(v)) dfs1(v,1);
for(int v:dian[1]) if(v==find(v)) dfs2(v,1),cun.push_back(v);
for(int x:cun)
{
if(!tong[cntrt[x]]) ccx.push_back(cntrt[x]);
tong[cntrt[x]]+=siz[x];
}
for(int x:ccx) for(int v:ccx) cf[v]=(cf[v]+tong[x]*x%mo*ksm(x+v,mo-2)%mo);
cout<<1<<' ';
for(int i=2;i<=n;i++) if(ans[i]) cout<<(ans[i]+cf[cntrt[find(i)]]-siz[find(i)]*ksm(2,mo-2)%mo+1+mo)%mo<<" "; else cout<<0<<" ";
return 0;
}