[题解]AT_abc287_f [ABC287F] Components

思路

定义 dpi,j,0/1 表示在以 i 为根的子树中(包括 i)选出 j 个连通块,且 i 不选/选 的方案数。

假设我们在 DFS 过程中,当前枚举到以 u 为根节点的情况,那么显然有 dpu,0,0=dpu,1,1=1

然后,我们从 u 开始向其儿子节点搜索,假设当前枚举到的点是 v

那么,我们对于每一个 v,我们都可以对 u 的选取情况作分类讨论(其中 a,b 表示以 u 为根的子树在没有 v 之前的连通块数量,和以 v 为根的子树中连通块的数量):

  1. 如果不选,那么两棵子树合并,连通块数量为 a+b,那么有 dpu,a+b,0dpu,a+b,0+dpu,a,0×(dpv,b,0+dpv,b,1)
  2. 如果选,那么还需要根据 v 的选取情况进行分类讨论:
    • 如果 v 不选,那么两棵子树合并,连通块数量还是为 a+b,那么有 dpu,a+b,1dpu,a+b,1+dpu,a,1×dpv,b,0
    • 如果 v 选,那么两棵子树合并,由于 u,v 都要选,那么,包含 u,v 的两个连通块将合并为一个,所以连通块数量为 a+b1,那么有 dpu,a+b1,1dpu,a+b1,1+dpu,a,1×dpv,b,1

Code

#include <bits/stdc++.h>  
#define int long long  
#define re register  
  
using namespace std;  
  
const int N = 5010,M = 1e4 + 10,mod = 998244353;  
int n;  
int idx,h[N],e[M],ne[M];  
int dp[N][N][2];  
  
inline int read(){  
    int r = 0,w = 1;  
    char c = getchar();  
    while (c < '0' || c > '9'){  
        if (c == '-') w = -1;  
        c = getchar();  
    }  
    while (c >= '0' && c <= '9'){  
        r = (r << 1) + (r << 3) + (c ^ 48);  
        c = getchar();  
    }  
    return r * w;  
}  
  
inline void add(int a,int b){  
    ne[idx] = h[a];  
    e[idx] = b;  
    h[a] = idx++;  
}  
  
inline int dfs(int u,int fa){  
    int sz = 1;  
    dp[u][0][0] = dp[u][1][1] = 1;  
    for (re int i = h[u];~i;i = ne[i]){  
        int j = e[i];  
        if (j == fa) continue;  
        int ssz = dfs(j,u);  
        for (re int a = sz;~a;a--){  
            for (re int b = ssz;b;b--) dp[u][a + b][0] = (dp[u][a + b][0] + dp[u][a][0] * (dp[j][b][0] + dp[j][b][1]) % mod) % mod;  
            // b 不能枚举到 0,因为如果 b 枚举到 0,那么将会有 dp[u][a][0] = 2 * dp[u][a][0] % mod,然而这种状态肯定是我们计算过的,所以不能枚举到 0   
        }  
        for (re int a = sz;a;a--){  
            for (re int b = ssz;b;b--){  
                dp[u][a + b][1] = (dp[u][a + b][1] + dp[u][a][1] * dp[j][b][0] % mod) % mod;  
                dp[u][a + b - 1][1] = (dp[u][a + b - 1][1] + dp[u][a][1] * dp[j][b][1] % mod) % mod;  
            }  
        }  
        sz += ssz;  
    }  
    return sz;  
}  
  
signed main(){  
    memset(h,-1,sizeof(h));  
    n = read();  
    for (re int i = 1;i < n;i++){  
        int a,b;  
        a = read();  
        b = read();  
        add(a,b);  
        add(b,a);  
    }  
    dfs(1,-1);  
    for (re int i = 1;i <= n;i++) printf("%lld\n",(dp[1][i][0] + dp[1][i][1]) % mod);  
    return 0;  
}  

作者:WaterSun

出处:https://www.cnblogs.com/WaterSun/p/18262937

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   WBIKPS  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示