AT_abc287_f [ABC287F] Components 题解

题意

给你一棵有 NNN 个节点的树,顶点标号为 1,2,…,N1,2,\dots,N1,2,,N ,第 iii 条边连接的端点是 ai,bia_i,b_iai,bi.

对每一个 x=1,2,…,Nx=1,2,\dots,Nx=1,2,,N ,解决如下问题:

2N−12^N-12N1 个非空的顶点子集 VVV ,求 VVV 的刚好包含 xxx 个连通块的诱导子图的数量。

什么是诱导子图?

SSS 是图 GGG 的顶点的子集,SSS 的诱导子图是包含 SSS 的所有顶点和这些顶点之间的所有边,这些边的两个端点都必须在 SSS 中。

思路

不难看出这题是一道树形动态规划,我们可以用 dp(i,j,0)dp(i,j,0)dp(i,j,0) 表示在 iii 的子树上有 jjj 块并且不选第 iii 块的方案数,用 dp(i,j,1)dp(i,j,1)dp(i,j,1) 表示在 iii 的子树上有 jjj 块并且选第 iii 块的方案数。

由题意得,我们假如不选,则子节点可选可不选。如果选,则子节点要是选则选的块数是一样的,要是不选就要多加一块。

所以,我们可以分三类来讨论:

iii 点选择,可得:
dp(i,j+k,0)=∑x∈isondp(i,j,0)×(dp(x,k,1)+dp(x,k,0))dp(i,j+k,0)= \sum_{x\in ison} dp(i,j,0)\times (dp(x,k,1)+dp(x,k,0))dp(i,j+k,0)=xisondp(i,j,0)×(dp(x,k,1)+dp(x,k,0))
iii 点不选则且 iii 点的子节点也不选,可得:
dp(i,j+k−1,1)=∑x∈isondp(i,j,1)×dp(x,k,1)dp(i,j+k-1,1)= \sum_{x\in ison} dp(i,j,1)\times dp(x,k,1)dp(i,j+k1,1)=xisondp(i,j,1)×dp(x,k,1)
iii 点不选则且 iii 点的子节点选,可得:
dp(i,j+k,1)=∑x∈isondp(i,j,1)×dp(x,k,0)dp(i,j+k,1)= \sum_{x\in ison} dp(i,j,1)\times dp(x,k,0)dp(i,j+k,1)=xisondp(i,j,1)×dp(x,k,0)

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n;
struct op {
    int from, to, next;//链式前向星
} a[10005];
int head[5005], cnt;
void add(int x, int y) { a[cnt].from = x, a[cnt].to = y, a[cnt].next = head[x], head[x] = cnt++; }//链式前向星存图 
const int mod = 998244353;
int son_sum[5005];
int dp[5005][5005][2];
bool vis[5005];
void dfs(int x, int father) {
    dp[x][0][0] = dp[x][1][1] = 1;//初始化 
    son_sum[x] = 1;//初始化 
    for (int i = head[x]; i != -1; i = a[i].next) {
        int y = a[i].to;
        if (y == father)
            continue;
        dfs(y, x);
        for (int j = son_sum[x]; j >= 0; j--) {
            for (int k = son_sum[y]; k >= 1; k--) {
            	//根据dp式 
                dp[x][j + k][0] =
                    ((dp[x][j + k][0]) + ((dp[x][j][0] * ((dp[y][k][1] + dp[y][k][0]) % mod)) % mod)) % mod;
                if (j == 0)
                    continue;
                dp[x][j + k][1] = ((dp[x][j + k][1]) + ((dp[x][j][1] * dp[y][k][0]) % mod)) % mod;
                dp[x][j + k - 1][1] = ((dp[x][j + k - 1][1]) + ((dp[x][j][1] * dp[y][k][1]) % mod)) % mod;
            }
        }
        son_sum[x] += son_sum[y];//记录当前子树一共有多少个点 
    }
}
signed main() {
    freopen("block.in", "r", stdin);
    freopen("block.out", "w", stdout);
    memset(head, -1, sizeof(head));
    scanf("%lld", &n);
    for (int i = 1, x, y; i < n; i++) {
        scanf("%lld%lld", &x, &y);
        add(x, y), add(y, x);//建边 
    }
    dfs(1, -1);
    for (int i = 1; i <= n; i++) {
        printf("%lld\n", (dp[1][i][0] + dp[1][i][1]) % mod);//输出答案,记得取模 
    }
    return 0;
}
### 关于洛谷 AT_ABC219_C Neo-lexicographic Ordering 的解题思路 对于给定的新字母顺序字符串 \(X\) 和一系列单词列表,目标是在新定义的字典序下对这些单词进行排序。此问题的核心在于理解如何依据自定义的字符优先级来比较两个字符串。 #### 字符串比较逻辑 为了按照新的字母表顺序排列字符串数组,需先构建一个映射关系,该映射能够将原始英文字母转换为对应的新位置编号。这可以通过遍历输入的字符串 \(X\) 来完成,并记录每个字符的位置索引作为其权重值[^4]。 ```python def create_mapping(order_string): mapping = {} for index, char in enumerate(order_string): mapping[char] = index return mapping ``` 有了这个映射之后,在对比任意两个字符串时就可以逐位检查它们对应的数值大小来进行判断: ```python def compare_strings(s1, s2, map_): min_length = min(len(s1), len(s2)) for i in range(min_length): if map_[s1[i]] != map_[s2[i]]: return -1 if map_[s1[i]] < map_[s2[i]] else 1 # If all characters are equal up to the length of shorter one, # then longer string should come after. if len(s1) == len(s2): return 0 elif len(s1) < len(s2): return -1 else: return 1 ``` 最后一步就是应用上述函数去调整整个字符串集合内的元素次序了。可以采用内置排序方法并指定 `key` 参数指向辅助比较功能;或者直接调用 Python 内置 sorted 函数配合 cmp_to_key 工具简化操作流程[^2]。 ```python from functools import cmp_to_key words.sort(key=cmp_to_key(lambda x,y :compare_strings(x, y, mapping))) print("\n".join(words)) ``` 以上即是对 ABC219C 题目——Neo-Lexicographical Order 的基本求解框架描述。值得注意的是实际编码过程中还需注意边界条件处理以及效率优化等问题。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值