ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AtCoder竞赛题解:字符串删除操作与组合数学应用

AtCoder竞赛题解:字符串删除操作与组合数学应用 1. 题目解析与核心思路这道题目来自AtCoder Grand Contest 040编号为C题。题目要求我们统计所有长度为NN为偶数的由A、B、C组成的字符串中能够通过特定删除操作变为空串的数量。具体来说每次操作可以选择删除任意两个连续的字符但不能删除AB或BA这两种组合。1.1 问题转化技巧直接处理不能删除AB或BA的条件比较困难这里有一个巧妙的转化思路将字符串中偶数位置的所有A和B互换。具体来说对于原字符串s构造新字符串s其中s[i] s[i]当i为奇数s[i] B当s[i]A且i为偶数s[i] A当s[i]B且i为偶数s[i] C当s[i]C且i为偶数经过这样的变换后原问题中的限制条件不能删除AB或BA就变成了不能删除AA或BB。这是因为原字符串中的AB或BA组合在变换后会变成AA或BB其他组合如AC、BC等则保持原有性质或变成其他允许的组合这个转化是一一对应的因此我们可以转而计算变换后字符串中满足不能删除AA或BB条件的数量。1.2 关键观察与条件分析经过上述转化后我们需要计算的是在变换后的字符串中能够通过不断删除非AA且非BB的两个连续字符最终将字符串清空的数量。这里有一个重要的观察一个字符串不能被完全删除当且仅当其中A的数量或B的数量超过字符串长度的一半即≥N/2。这是因为如果A的数量≥N/2那么至少有两个A会相邻根据鸽巢原理形成不能删除的AA组合同理如果B的数量≥N/2也会形成不能删除的BB组合只有当A和B的数量都严格小于N/2时才能保证总能找到可删除的字符对因此我们可以用总字符串数减去不满足条件的字符串数来得到答案。2. 组合数学解法详解2.1 总体思路与公式推导设N为偶数总共有3^N个可能的字符串每个位置有3种选择。我们需要从中减去那些A的数量≥N/2或B的数量≥N/2的字符串。根据容斥原理非法字符串的数量为 非法数量 (A≥N/2的数量) (B≥N/2的数量) - (A≥N/2且B≥N/2的数量)由于A和B的数量不可能同时≥N/2因为AB≤N而N/2N/2N此时C的数量必须为0所以最后一项为0。因此非法数量 (A≥N/2的数量) (B≥N/2的数量)由于A和B的情况对称我们只需要计算其中一种然后乘以2即可。2.2 组合数计算对于A的数量≥N/2的情况我们可以枚举A的数量k从N/21到N然后计算对应的字符串数量对于固定的kA的数量剩余N-k个位置可以是B或C每个有2种选择。因此数量为C(N,k) * 2^{N-k}其中C(N,k)是组合数表示从N个位置中选择k个位置放A。因此总非法数量为 sum 2 * Σ_{kN/21}^N C(N,k) * 2^{N-k}最终答案为 ans 3^N - sum2.3 模运算处理由于N可以达到1e7结果需要对998244353取模我们需要高效计算组合数和幂次。3. 算法实现与优化3.1 预处理阶乘和逆元为了高效计算组合数C(N,k) N! / (k! * (N-k)! ) mod 998244353我们需要预处理阶乘数组mul[i] i! mod 998244353阶乘的逆元数组inv[i] (i!)^-1 mod 998244353计算逆元可以使用费马小定理因为998244353是质数 a^{-1} ≡ a^{mod-2} mod mod3.2 快速幂实现我们需要实现快速幂函数来计算幂次和逆元long long q_pow(long long u, long long v) { long long res 1ll; while(v) { if(v 1ll) res res * u % mod; u u * u % mod; v 1; } return res; }3.3 组合数计算函数预处理阶乘和逆元后组合数可以O(1)计算long long C(int u, int v) { return mul[u] * inv[v] % mod * inv[u - v] % mod; }3.4 主算法流程预处理阶乘、逆元和2的幂次计算总字符串数3^N mod 998244353计算非法字符串数量sum输出(3^N - 2*sum) mod 9982443534. 完整代码解析#include bits/stdc.h using namespace std; const int N 10000010; const long long mod 998244353ll; int n; long long mul[N 10], inv[N 10], pow2[N 10]; // 快速幂函数 long long q_pow(long long u, long long v) { long long res 1ll; while(v) { if(v 1ll) res res * u % mod; u u * u % mod; v 1; } return res; } // 预处理阶乘、逆元和2的幂次 void init() { mul[0] inv[0] pow2[0] 1ll; for(int i 1; i N; i) { mul[i] mul[i - 1] * (long long)i % mod; pow2[i] pow2[i - 1] * 2ll % mod; } inv[N] q_pow(mul[N], mod - 2ll); for(int i N - 1; i 0; --i) { inv[i] inv[i 1] * (long long)(i 1) % mod; } } // 组合数计算 long long C(int u, int v) { return mul[u] * inv[v] % mod * inv[u - v] % mod; } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin n; init(); long long ans q_pow(3ll, n), sum 0ll; // 计算非法情况总和 for(int i n / 2 1; i n; i) { sum (sum C(n, i) * pow2[n - i] % mod) % mod; } // 最终答案 cout (ans - 2ll * sum % mod mod) % mod; return 0; }5. 复杂度分析与优化5.1 时间复杂度预处理阶乘、逆元和2的幂次O(N)计算3^NO(log N)快速幂计算sumO(N/2) ≈ O(N)总时间复杂度O(N)5.2 空间复杂度需要存储阶乘、逆元和2的幂次数组每个大小都是N1因此空间复杂度为O(N)5.3 优化技巧预处理范围只需要到N不需要到1e7可以边计算阶乘边计算2的幂次减少循环次数逆元计算可以从N倒推利用inv[i] inv[i1] * (i1) % mod6. 常见问题与调试技巧6.1 模运算注意事项减法取模(a - b) % mod 可能为负数需要加上mod再取模乘法取模两个1e9级别的数相乘可能溢出long long建议使用快速乘或先取模除法取模必须转换为乘以逆元6.2 边界条件处理N0时空字符串视为合法应返回1N2时只有AA和BB非法应返回3^2 - 2*1 7需要验证N的奇偶性题目保证N为偶数6.3 调试技巧对小数据手工计算验证打印中间结果如阶乘、逆元值检查是否正确使用assert验证关键条件如组合数性质7. 算法扩展与变种7.1 奇数长度的情况如果N为奇数问题会变得复杂因为最后会剩下一个字符。需要考虑最终剩余字符的限制条件。7.2 更多字符限制如果字符集扩大如加入D、E等或者禁止删除的组合增多可能需要更复杂的容斥原理应用。7.3 不同删除规则如果删除规则变化如可以删除任意长度的子串或者有更多限制条件可能需要完全不同的解法如动态规划。在实际编程比赛中遇到这类问题时关键在于发现问题的对称性和可转化性。这道题的巧妙之处在于通过字符位置的变换将复杂条件简化为更易处理的形式。对于类似的字符串操作问题尝试寻找不变量或进行恰当的转化往往是解题的关键。
返回列表