AGC058F
被震撼到了。非常好人类智慧题,使我大小为
思路
注意到,如果将
可是这个题不是
既然我们改变不了数量,那我们就让它从删边改成删点不就是
这个时候厉害的来了,我们给每个边点连
于是我们解决了
于是,我们需要钦定所有边点的删除顺序先于周围的所有点,这样每次删除的时候所有分出来的树的大小都与原来树上对应部分的大小模
我们给这个顺序建立拓补图,发现有的限制是儿子先于父亲,有的限制是父亲先于儿子,很不好算。考虑容斥,钦定每个儿子先于父亲的限制变成父亲先于儿子或者不限制,这下所有边的方向就相同了。
令
最后,每个点在外向树内必然是第一个被删除的,即
可是每个边点还有
根据容斥,最终答案为
使用树形背包的合并方式(即只卷到
代码
#include<bits/stdc++.h>
using namespace std;
const int NN=5004,P=998244353;
vector<int>g[NN];
int f[NN][NN],siz[NN],inv[NN];
void dfs(int u,int fa)
{
f[u][1]=1;
siz[u]=1;
for(auto v:g[u])
{
if(v==fa)
continue;
dfs(v,u);
for(int i=siz[u];i;i--)
{
int res=0;
for(int j=1;j<=siz[v];j++)
{
f[u][i+j]=(f[u][i+j]-1ll*f[u][i]*f[v][j]%P*inv[j]%P+P)%P;
res=(res+1ll*f[v][j]*inv[j]%P)%P;
}
f[u][i]=1ll*f[u][i]*res%P;
}
siz[u]+=siz[v];
}
for(int i=1;i<=siz[u];i++)
f[u][i]=1ll*f[u][i]*inv[i]%P;
}
int main()
{
int n;
scanf("%d",&n);
inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=1ll*(P-P/i)*inv[P%i]%P;
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,0);
int res=0;
for(int i=1;i<=n;i++)
res=(res+f[1][i])%P;
printf("%d",res);
return 0;
}