D-Rebuild Tree
Prufer 是这样建立的:每次选择一个编号最小的叶结点并删掉它,然后在序列中记录下它连接到的那个结点。重复 n − 2 n-2 n−2次后就只剩下两个结点,算法结束。(为什么不是 n − 1 n-1 n−1次呢?因为第 n − 1 n-1 n−1次操作序列记录下的节点一定是 n n n)
一个 n n n个点 m m m条边的带标号无向图有 k k k个连通块。我们希望添加 k − 1 k-1 k−1条边使得整个图连通。方案数为 n k − 2 ⋅ ∏ i = 1 k s i n^{k-2}·\prod_{i=1}^{k}s_i nk−2⋅i=1∏ksi
证明考虑组合意义,详细见 OIWIKIPrufer 序列
- 有了上面结论,删
k
k
k条边之后形成
k
+
1
k+1
k+1个连通块,设每个连通块的大小为
s
i
s_i
si
则生成树个数为 n k − 1 ⋅ ∏ i = 1 k + 1 s i n^{k-1}·\prod_{i=1}^{k+1}s_i nk−1⋅∏i=1k+1si,该题就是求 ∑ split(n,k) n k − 1 ⋅ ∏ i = 1 k + 1 s i = n k − 1 ⋅ ∑ split(n,k) ∏ i = 1 k + 1 s i \sum_{\text{split(n,k)}}n^{k-1}·\prod_{i=1}^{k+1}s_i=n^{k-1}·\sum_{\text{split(n,k)}}\prod_{i=1}^{k+1}s_i split(n,k)∑nk−1⋅i=1∏k+1si=nk−1⋅split(n,k)∑i=1∏k+1si - 求 ∑ split(n,k) ∏ i = 1 k + 1 s i \sum_{\text{split(n,k)}}\prod_{i=1}^{k+1}s_i ∑split(n,k)∏i=1k+1si可以考虑将问题转化为等价问题:删掉 k k k条边且在每个联通块选一个点的方案数(由于每个连通块有 s i s_i si种选择即得出 ∏ i = 1 k + 1 s i \prod_{i=1}^{k+1}s_i ∏i=1k+1si)。
设计dp:
f
u
,
j
,
0
/
1
f_{u,j,0/1}
fu,j,0/1表示
u
u
u子树内,删了
j
j
j条边,是否选择点的方案数。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
template <class T=int> T rd()
{
T res=0;T fg=1;
char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}
while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
return res*fg;
}
const int N=50010,mod=998244353;
int n,m;
vector<int> e[N];
ll qmi(ll a,ll b)
{
ll v=1;
while(b)
{
if(b&1) v=v*a%mod;
b>>=1;
a=a*a%mod;
}
return v;
}
ll f[N][105][2];
ll g[105][2];
int sz[N];
void dfs(int u,int fa)
{
sz[u]=1;
f[u][0][0]=f[u][0][1]=1;
for(auto v:e[u])
{
if(v==fa) continue;
dfs(v,u);
memset(g,0,sizeof g);
for(int i=0;i<=min(sz[u]-1,m);i++)
for(int j=0;j<sz[v]&&i+j<=m;j++)
{
g[i+j][0]=(g[i+j][0]+f[u][i][0]*f[v][j][0]%mod)%mod;
g[i+j][1]=(g[i+j][1]+f[u][i][0]*f[v][j][1]%mod+f[u][i][1]*f[v][j][0]%mod)%mod;
if(i+j==m) continue;
g[i+j+1][0]=(g[i+j+1][0]+f[u][i][0]*f[v][j][1]%mod)%mod;
g[i+j+1][1]=(g[i+j+1][1]+f[u][i][1]*f[v][j][1]%mod)%mod;
}
sz[u]+=sz[v];
memcpy(f[u],g,sizeof g);
}
}
int main()
{
n=rd(),m=rd();
for(int i=1;i<n;i++)
{
int u=rd(),v=rd();
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,0);
printf("%lld\n",f[1][m][1]*qmi(n,m-1)%mod);
}