【Nowcoder】蓝魔法师 | 树形dp、组合计数

题目链接:https://ac.nowcoder.com/acm/problem/20811

题目大意:

给出n个点的一棵树,问有多少种删边方案使得删边后各连通块大小小于等于k?

题目思路:

考虑树形dp与组合数学结合

定义dp状态 dp(i,k) 代表 i的子树全部合法且i的连通块大小是k

那么显然对于任意一个节点u来说初始:dp[u][1] = 1

接下来枚举每一条边,对于一条边来言有删除与不删除两种状态:

1.删除:

删除此边,那么就意味着当前以u节点连通块大小为k的方案数 都可以 v节点连通块大小所有的方案数:

dp[u][k] = dp[u][k]*\sum_{j=1}^{j=sz[e]}{dp[e][j]}

2.不删除

不删除就相当于合并那么此时直接跑两个循环即可:

        for(int i=1;i<=min(sz[u]*1ll,m);i++){
            for(int k=1;k<=min(sz[e]*1ll,m);k++){
                if(i+k<=m){
                    tmp[i+k] += (dp[u][i]*dp[e][k])%mod;
                    tmp[i+k] %=mod;
                }
            }
        }

考虑到和dp[u][k]的状态有关,所以用tmp数组先预初储存一下!

Code:

/*** keep hungry and calm CoolGuang!***/
ll n,m,p;
ll dp[2005][2005];
vector<int>v[maxn];
int sz[maxn];
ll tmp[maxn];
void dfs(int u,int fa){
    dp[u][1] = 1;
    sz[u] = 1;
    for(int e:v[u]){
        if(e == fa) continue;
        dfs(e,u);
        ll sum = 0;
        ///连边
        for(int i=1;i<=min(sz[u]*1ll,m);i++){
            for(int k=1;k<=min(sz[e]*1ll,m);k++){
                if(i+k<=m){
                    tmp[i+k] += (dp[u][i]*dp[e][k])%mod;
                    tmp[i+k] %=mod;
                }
            }
        }
        ///不连边
        for(int i=1;i<=min(m,sz[e]*1ll);i++) sum = (sum + dp[e][i])%mod;
        for(int i=1;i<=m;i++){
            dp[u][i] = (tmp[i] + sum*dp[u][i])%mod;
            tmp[i] = 0;
        }
        sz[u] += sz[e];
    }
}
int main(){
    read(n);read(m);
    for(int i=1;i<=n-1;i++){
        ll x,y;read(x);read(y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    dfs(1,1);
    ll ans = 0;
    for(int k=1;k<=m;k++) ans = (ans + dp[1][k])%mod;
    printf("%lld\n",ans);
    return 0;
}
/***
3 3
1 2
1 3

***/

补充:

最后对于复杂度的证明:

我试过无数种写法,只有这一种在sz[e]和sz[u]的情况下过了

这种写法的复杂度其实是O(n^2)的

考虑lca的贡献次数即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只酷酷光儿( CoolGuang)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值