CF1060F Shrinking Tree 题解
思路
主要是难以理清思维链条。看题解也感觉有点难理解。主要是状态设计的抽象性以及状态转移的隐性。
首先先概率转总和。我们考虑对于每一种删去边的方案去求其剩下的编号是 \(rt\)(每次钦定的根)的概率。这样两个互相影响的随机事件就变成了一个,方便统计。
我们将删去边的意义抽象成将两端的颜色合为同一种颜色。
设 \(f_{u,i}\) 表示对于 \(u\) 的子树,对于所有删完子树中的边的情况,只考虑其最后 \(i\) 次删去边的选择的颜色是什么,使得保证 \(u\) 点的颜色仍是其自己的概率。倒数第 \(i\) 次之前的随便取,我们认为其不重要。
那么答案就是 \(\frac {f_{rt,n-1}} {(n-1)!}\)。
考虑如何合并答案。设 \(v\) 是 \(u\) 的一个儿子。由于我们现在要转移 \(f_u\),因此我们就要考虑 \((u,v)\) 这条边选的什么颜色。
我们枚举一个 \(i,j\)。设 \(g_{v,i}\) 表示删 \(v\) 及其子树与 \(u\) 构成的树,只考虑删去最后 \(i\) 次删的边选择的颜色使得保证 \(u\) 的颜色仍然是其自己的概率。也就是与 \(f\) 类似的定义。
考虑 \(g_{v,i}\) 从 \(f_{v,j}\) 转移。考虑分情况。如果 \(i<j\),那么显然这个时候根据 \(f\) 的定义已经保证了 \(u\) 的颜色,于是直接有 \(g_{v,i}\gets f_{v,i}\)。否则,就没有保证 \((u,v)\) 这条边选择的颜色,那么就有 \(g_{v,i}\gets \frac 1 2 f_{v,j}\)。
然后考虑 \(g\) 与 \(u\) 之前已经算完的子树的合并。由于我们只是规定了最后几个边删除的相对顺序,因此有
最后考虑第一个求 \(g\) 的转移可以用前缀和优化,第二部分是树上背包合并,总复杂度为 \(O(n^3)\)。
code
这里没有写浮点数的版本,而是对 998244353 取模。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=505,p=998244353;
int add(const int &x,const int &y){return x+y<0?x+y+p:(x+y>p?x+y-p:x+y);}
void upd(int &x,const int &y){x=add(x,y);}
int ksm(int x,int k){
int res=1;x%=p;while(k){
if(k&1)res=res*x%p;
x=x*x%p;k>>=1;
}return res;
}
int inv(int x){return ksm(x,p-2);}
const int inv2=inv(2);
vector <int> q[N];
int n,f[N][N],g[N],siz[N],pw[N],ni[N],tmp[N],s[N];
int C(int x,int y){
if(y>x)return 0;
return pw[x]*ni[y]%p*ni[x-y]%p;
}
void dfs(int u,int fa){
siz[u]=1;f[u][0]=1;
for(int v:q[u]){
if(v==fa)continue;dfs(v,u);memset(g,0,sizeof(g));
for(int i=0;i<=siz[v];i++){
if(i>=0)upd(g[i],inv2*s[i-1]%p);
upd(g[i],(siz[v]-i)*f[v][i]%p);
}
memset(tmp,0,sizeof(tmp));
for(int i=siz[u]-1;i>=0;i--){ //这种关于子树大小的树形背包一次的总复杂度是 n^2 的
for(int j=siz[v];j>=0;j--){
upd(tmp[i+j],C(i+j,j)*C(siz[u]-i-1+siz[v]-j,siz[u]-i-1)%p*g[j]%p*f[u][i]%p);
}
}
for(int i=0;i<siz[u]+siz[v];i++)f[u][i]=tmp[i];
siz[u]+=siz[v];
}
s[0]=f[u][0];for(int i=1;i<=siz[u];i++)s[i]=add(s[i-1],f[u][i]);//
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n;for(int i=1,u,v;i<n;i++)cin>>u>>v,q[u].push_back(v),q[v].push_back(u);
pw[0]=1;for(int i=1;i<=n;i++)pw[i]=pw[i-1]*i%p;ni[n]=inv(pw[n]);
for(int i=n-1;i>=0;i--)ni[i]=ni[i+1]*(i+1)%p;
for(int i=1;i<=n;i++)memset(f,0,sizeof(f)),dfs(i,0),cout<<(f[i][n-1]*ni[n-1]%p)<<'\n';
return 0;
}

浙公网安备 33010602011771号