牛客-蓝魔法师(树形dp)

题目
在这里插入图片描述
题解:

设dp[i][j]表示以 i 为根的子树,选j个点保持连通的方案数。
那么可以列出转移方程:

for(int j=min(num[u],k);j>=1;j--)//枚举之前选了多少个点
    for(int z=min(k,num[v]);z>=1;z--)//枚举当前子节点选了多少个点
        dp[u][j+z]=dp[u][j+z]+dp[u][j]*dp[v][z];

转移过程类似于背包,而且必须是从大枚举到小。

还要注意的是,子节点这个子树也可能不选,那么状态转移方程:

for(int j=1;j<=num[v]&&j<=k;j++) sum=(sum+dp[v][j]);
dp[u][j]=dp[u][j]*sum;

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int>pii;
const int MAXN=2e3+5;
const int mod=998244353;
const int inf=0x3f3f3f3f;
const double pi=acos(-1);
int n,k;
int head[MAXN];
ll dp[MAXN][MAXN];
int num[MAXN];
struct node
{
    
    
    int to;
    int next;
}e[MAXN<<1];
int cnt=0;
void add(int u,int v){
    
    
    e[cnt].to=v;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
void dfs(int u,int f){
    
    
    num[u]=1;
    dp[u][1]=1;
    for(int i=head[u];i!=-1;i=e[i].next){
    
    
        int v=e[i].to;
        if(v==f) continue;
        dfs(v,u);
        ll sum=0;
        for(int j=1;j<=num[v]&&j<=k;j++) sum=(sum+dp[v][j])%mod;
        for(int j=min(num[u],k);j>=1;j--){
    
    
            for(int z=min(k,num[v]);z>=1;z--){
    
    
                if(j+z>k) continue;
                dp[u][j+z]=(dp[u][j+z]+dp[u][j]*dp[v][z]%mod)%mod;
            }
            dp[u][j]=dp[u][j]*sum%mod;
        }
        num[u]+=num[v];
    }
}
int main(){
    
    
    memset(head,-1,sizeof head);
    cin>>n>>k;
    for(int i=1;i<=n-1;i++){
    
    
        int u,v;
        cin>>u>>v;
        add(u,v);
        add(v,u);
    }
    dfs(1,-1);
    ll ans=0;
    for(int i=1;i<=k&&i<=num[1];i++){
    
    
        ans=(ans+dp[1][i])%mod;
    }
    printf("%lld\n",ans);
}

猜你喜欢

转载自blog.csdn.net/weixin_45755679/article/details/113664354