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]);
}

猜你喜欢

转载自blog.csdn.net/jacajava/article/details/80113102