Nowcoder20811.蓝魔法师(树形背包)

“你,你认错人了。我真的,真的不是食人魔。”--蓝魔法师

给出一棵树,求有多少种删边方案,使得删后的图每个连通块大小小于等于k,两种方案不同当且仅当存在一条边在一个方案中被删除,而在另一个方案中未被删除,答案对998244353取模

#include<bits/stdc++.h>
using namespace std;
const int maxn=2005;
const int mod=998244353;
typedef long long ll; 
ll f[maxn][maxn];
//f(i,j)表示i的子树内i本身的连通块大小为j的方案数
//不如换成每个点独立,有多少种连边方案
//那么f(i,1)一开始是1
//对i的每个儿子v,可以选择连边,那么f(i,j+k)+=f(i,j)*f(v,k)
//也可以选择不连边,那么f(i,j)*=f(v,k)

vector<int> g[maxn];
int size[maxn];
int n,m;
void dfs (int x,int pre) {
	f[x][1]=1;
	size[x]=1;
	for (int y:g[x]) {
		if (y==pre) continue;
		dfs(y,x);
		long long sum=0;
		for (int i=1;i<=min(m,size[y]);i++) {
			sum=(sum+f[y][i])%mod;
		}
		for (int i=min(m,size[x]);i>=1;i--) {
			for (int j=min(m,size[y]);j>=1;j--) {
				if (i+j<=m) {
					f[x][i+j]=(f[x][i+j]+f[x][i]*f[y][j]%mod)%mod;
				}
			}
			f[x][i]*=sum;
			f[x][i]%=mod;
		}
		size[x]+=size[y];
	}
} 
int main () {
	scanf("%d%d",&n,&m);
	for (int i=1;i<n;i++) {
		int x,y;
		scanf("%d%d",&x,&y);
		g[x].push_back(y);
		g[y].push_back(x);
	}
	dfs(1,0);
	ll ans=0;
	for (int i=1;i<=m;i++) ans=(ans+f[1][i])%mod;
	printf("%lld\n",ans);
}
posted @ 2021-04-01 12:35  zlc0405  阅读(44)  评论(0编辑  收藏  举报