树上删掉一些边后使图连通的方案数

链接:https://www.nowcoder.com/acm/contest/215/C
来源:牛客网

题目描述

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

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

输入描述:

第一行两个整数n,k, 表示点数和限制
2 <= n <= 2000, 1 <= k <= 2000
接下来n-1行,每行包括两个整数u,v,表示u,v两点之间有一条无向边
保证初始图联通且合法

输出描述:

共一行,一个整数表示方案数对998244353取模的结果
示例1

输入

复制
5 2
1 2
1 3
2 4
2 5

输出

复制
7

思路分析 :
  定义dp[i][j] 表示 i 结点所在的连通块中节点数为 j 的方案数是多少

代码示例 :
  
#define ll long long

ll n, kk;
vector<ll>ve[2005];
ll dp[2005][2005];
ll size[2005], num[2005];

void dfs(ll x, ll fa){
    dp[x][1] = 1;
    size[x] = 1;
    
    for(ll i = 0; i < ve[x].size(); i++){
        ll to = ve[x][i];
        if (to == fa) continue;
        dfs(to, x);
        
        for(ll j = 1; j <= size[x]; j++){
            for(ll k = 0; k <= size[to]; k++){
                if (j+k > kk) break;
                num[j+k] += dp[x][j]*dp[to][k];
                num[j+k] %= mod;
            }
        }
        size[x] += size[to];
        for(ll j = 1; j <= size[x]; j++) dp[x][j] = num[j], num[j] = 0;
    }
    for(ll i = 1; i <= kk; i++){
        dp[x][0] = (dp[x][0]+dp[x][i])%mod;
    }
}

int main() {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    cin >> n >> kk;
    ll x, y;
    
    for(ll i = 1; i < n; i++){
        scanf("%lld%lld", &x, &y);
        ve[x].push_back(y);
        ve[y].push_back(x);
    }    
    dfs(1, 0);
    ll ans = 0;
    for(ll i = 1; i <= kk; i++) ans = (ans+dp[1][i])%mod;
    printf("%lld\n", ans);
    return 0;
}

 

posted @ 2018-10-30 17:02  楼主好菜啊  阅读(1194)  评论(0编辑  收藏  举报