蓝魔法师

蓝魔法师

https://ac.nowcoder.com/acm/problem/20811

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

思路:
树形dp:
从叶子节点向根转移,dp[i][j]表示以i为根的树删边时i处于的连通块大小为j的方案数。
sum[i]表示以i为根的树使得删后的图每个连通块大小小于等于k的总方案数。
sz[i]表示以i为根的树的节点数。
当儿子v向父亲u转移时有两种情况:
①父子节点在同一连通块:
dp[u][i+j]+=dp[u][i]*dp[v][j];
①父子节点不在同一连通块:
dp[u][i]=dp[u][i] * sum[v];
注意先用一个中间变量保存,防止重复。
最后结果为sum[root].

代码:

#include <bits/stdc++.h>
typedef long long ll;

using namespace std;

const ll inf=998244353;

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}

int n, k ,sz[2005];
ll dp[2005][2005], pa[2005];
ll sum[2005], ans=0;
vector<int> g[2005];

void dfs(int v,int f)
{
    dp[v][1]=1;
    sz[v]=1;
    for(int i=0; i<g[v].size(); i++)
    {
        if(g[v][i]!=f)
        {
            dfs(g[v][i],v);
            for(int j=min(k,sz[v]); j; j--)
            {
                for(int o=min(sz[g[v][i]],k); o; o--)
                {
                    if(j+o<=k)
                    {
                        pa[j+o]=(pa[j+o]+dp[v][j]*dp[g[v][i]][o]%inf)%inf;
                    }
                }
                pa[j]=(pa[j]+dp[v][j]*sum[g[v][i]]%inf)%inf;
            }
            sz[v]+=sz[g[v][i]];
            for(int j=1; j<=min(k,sz[v]); j++)
            {
                dp[v][j]=pa[j]%inf;
                pa[j]=0;
            }
        }
    }
    for(int i=1; i<=min(k,sz[v]); i++)
    {
        sum[v]=(sum[v]+dp[v][i])%inf;
    }
    return ;
}

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1; i<n; i++)
    {
        int u, v;
        scanf("%d%d",&u,&v);
        g[v].push_back(u);
        g[u].push_back(v);
    }
    dfs(1,-1);
    cout << sum[1] << endl;
    return 0;
}
全部评论

相关推荐

2 收藏 评论
分享
牛客网
牛客企业服务