2021牛客多校#4 D-Rebuild Tree(Prufer序列)(树形DP)

D e s c r i p t i o n \mathscr{Description} Description

一棵树删k条边,再加k条边还是树,求方案数

S o l u t i o n \mathscr{Solution} Solution

前置知识:prufer序列

  • 设有k个连通块,k-1条边,则prufer序列长度为k-2
  • 每个位置可填1~n ⇒ n k − 2 \Rightarrow n^{k-2} nk2
  • 每条边联通连通块时可以连 s i s_i si个节点中任意一个
  • 综上可得

n k − 2 ∗ ∏ i = 1 k s i n^{k-2}*\mathop{\prod}\limits^{k}_{i=1}s_i nk2i=1ksi (k为联通块数量, s i s_i si为联通块大小)

  • 本题中 n k − 1 ∗ ∏ i = 1 k + 1 s i n^{k-1}*\mathop{\prod}\limits^{k+1}_{i=1}s_i nk1i=1k+1si(k条边k+1个联通块)

∏ i = 1 k s i \mathop{\prod}\limits^{k}_{i=1}s_i i=1ksi:树形DP

问题等价于 n n n点树分成 k + 1 k+1 k+1个连通块,并在每个连通块中任选一点的方案数
d p i , j , t dp_{i,j,t} dpi,j,t表示以i为根的子树中分为j个连通块,i点所在块中是否已选点方案数

{ t = 0 ⇒ 未 选 点 t = 1 ⇒ 已 选 点 \left\{ \begin{aligned} t & = 0 \Rightarrow 未选点 \\ t & = 1 \Rightarrow 已选点 \end{aligned} \right. {tt=0=1

for son
	for j i的联通块数
		for k son的连通块数

t a s k : 将 s o n 子 树 中 连 通 块 合 并 到 i 上 task:将son子树中连通块合并到i上 task:soni
在这里插入图片描述

C a s e     1 : i 块 中 的 点 在 i 的 祖 先 处 已 经 选 好 ⇒ d p i , j , 0 Case \,\,\,1:i块中的点在i的祖先处已经选好\Rightarrow dp_{i,j,0} Case1iidpi,j,0

  • i 与 s o n 不 删 边 ⇒ s o n 中 不 选 点 , 并 入 i 中 i与son 不删边 \Rightarrow son中不选点,并入i中 isonsoni
    ⟹    d p i , j , 0 + = d p i , j − k + 1 , 0 ∗ d p s o n , k , 0 \Longrightarrow\,\,dp_{i,j,0}+=dp_{i,j-k+1,0}*dp_{son,k,0} dpi,j,0+=dpi,jk+1,0dpson,k,0

  • i 与 s o n 删 边 , 不 管 s o n ⇒ s o n 中 必 须 选 点 ( 每 块 至 少 一 点 ) , 并 入 i 中 i与son删边,不管son\Rightarrow son中必须选点(每块至少一点),并入i中 isonsonsoni
    ⟹    d p i , j , 0 + = d p i , j − k , 0 ∗ d p s o n , k , 1 \Longrightarrow\,\,dp_{i,j,0}+=dp_{i,j-k,0}*dp_{son,k,1} dpi,j,0+=dpi,jk,0dpson,k,1

C a s e     2 : i 块 中 的 点 在 i 的 子 树 中 ⇒ d p i , j , 1 Case \,\,\,2:i块中的点在i的子树中\Rightarrow dp_{i,j,1} Case2iidpi,j,1

  • 不 删 边 , 点 在 i 这 块 中 不删边,点在i这块中 i
    ⟹    d p i , j , 1 + = d p i , j − k + 1 , 1 ∗ d p s o n , k , 0 \Longrightarrow\,\,dp_{i,j,1}+=dp_{i,j-k+1,1}*dp_{son,k,0} dpi,j,1+=dpi,jk+1,1dpson,k,0

  • 不 删 边 , 点 在 s o n 这 块 中 不删边,点在son这块中 son
    ⟹    d p i , j , 1 + = d p i , j − k + 1 , 0 ∗ d p s o n , k , 1 \Longrightarrow\,\,dp_{i,j,1}+=dp_{i,j-k+1,0}*dp_{son,k,1} dpi,j,1+=dpi,jk+1,0dpson,k,1

  • 删 边 隔 离 , i 和 s o n 中 都 有 点 删边隔离,i和son中都有点 ison
    ⟹    d p i , j , 1 + = d p i , j − k , 1 ∗ d p s o n , k , 1 \Longrightarrow\,\,dp_{i,j,1}+=dp_{i,j-k,1}*dp_{son,k,1} dpi,j,1+=dpi,jk,1dpson,k,1

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e4+10,K=110,M=998244353;
int n,k,sz[N],tot,h[N];
struct Edge{int v,nxt;}e[N<<1];
void add(int a,int b){e[++tot].v=b,e[tot].nxt=h[a],h[a]=tot;}
int dp[N][K][2],pre[K][2];
void dfs(int x,int fx){
	sz[x]=1;
	dp[x][1][0]=dp[x][1][1]=1;
	for(int i=h[x],v;i;i=e[i].nxt)if((v=e[i].v)!=fx)
	{
		dfs(v,x);
		memcpy(pre,dp[x],sizeof(pre));
		memset(dp[x],0,sizeof(dp[x]));
		for(int y=1;y<=min(sz[x],k+1);y++)
			for(int z=1;z<=min(sz[v],k+1)&&y+z-1<=k+1;z++)
				dp[x][y+z-1][0]=(dp[x][y+z-1][0]+1ll*pre[y][0]*dp[v][z][0])%M,
				dp[x][y+z][0]=(dp[x][y+z][0]+1ll*pre[y][0]*dp[v][z][1])%M,
				dp[x][y+z-1][1]=(dp[x][y+z-1][1]+1ll*pre[y][1]*dp[v][z][0])%M,
				dp[x][y+z-1][1]=(dp[x][y+z-1][1]+1ll*pre[y][0]*dp[v][z][1])%M,
				dp[x][y+z][1]=(dp[x][y+z][1]+1ll*pre[y][1]*dp[v][z][1])%M;
		sz[x]+=sz[v];
	}
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1,x,y;i<n;i++) 
		scanf("%d%d",&x,&y),add(x,y),add(y,x);
	dfs(1,0);
	int ans=dp[1][k+1][1];
	for(int i=1;i<k;i++)
		ans=1ll*ans*n%M;
	printf("%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值