51Nod1353 树
题意很简单,大概是有多少种删边方法使得每一块大小不小于k
我们设一个树形dp,f[i][j]表示i的子树中,i所在联通块的大小为j的方案数有多少
特别的,我们用f[i][0]表示∑f[i][j] (j>=k)
那么可以写出以下转移:
f[x][i+j]+=f[x][i]*f[v][j] (j>0)
f[x][i]=f[x][i]*f[v][0]
答案就是f[root][0]
#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define LL long long
#define M 1000000007
using namespace std;
struct edge{ int v,nt; } G[4010];
int h[2010],f[2010][2010],sz[2010],n,m,cnt=0;
inline void ad(int& x,LL y){ x=(x+y)%M; }
inline void adj(int x,int y){
G[++cnt]=(edge){y,h[x]}; h[x]=cnt;
G[++cnt]=(edge){x,h[y]}; h[y]=cnt;
}
void dp(int x,int p){
f[x][1]=sz[x]=1;
for(int v,i=h[x];i;i=G[i].nt)
if((v=G[i].v)!=p){
dp(v,x);
for(int i=sz[x];i;--i){
for(int j=sz[v];j;--j)
ad(f[x][i+j],(LL)f[x][i]*f[v][j]);
f[x][i]=(LL)f[x][i]*f[v][0]%M;
}
sz[x]+=sz[v];
}
for(int i=m;i<=sz[x];++i) ad(f[x][0],f[x][i]);
}
int main(){
scanf("%d%d",&n,&m);
for(int x,y,i=1;i<n;++i,adj(x,y)) scanf("%d%d",&x,&y);
dp(1,0);
printf("%d\n",f[1][0]);
}