CF1778D - Flexible String Revisit 题解

CF1778D - Flexible String Revisit

题面

给出两个长度均为 \(n(n\leq 10^6)\) 的 01 串 \(S\)\(T\)

每次随机将 \(S\) 中的某一位取反

问:第一次 \(S = T\) 时操作次数的期望


题解

成环期望的小 \(\text{trick}\),可以避免高斯消元和高阶递推。

如果我们按照经典的期望 \(dp\) 来设 \(f_i\) 表示当前还有 \(i\) 个字符未匹配:

\[f_i = \begin{cases} \frac{1}{n} f_1 + 1 & i = 0 \\ \frac{n - i + 1}{n} f_{i - 1} + \frac{i + 1}{n} f_{i + 1} + 1 & i \in [1, n) \\ \frac{n - 1}{n} f_{n - 1} + 1 & i = n \end{cases} \]

必须要使用二阶递推求系数或者建立系数矩阵进行高斯消元,我们不妨从定义上去改变原有的式子,使得关系式只与一项相关,考虑如下定义:

\(f_i\) 表示只剩下 \(i\) 个字符未匹配且本次操作使得一个位匹配的期望操作次数。

由此定义的 \(f_i\) 仅由 \(f_i\) 本身和 \(f_{i + 1}\) 决定,自己匹配的概率是 \(\frac{i}{n}\),否则会取消一个位的匹配,既然取消了我们必须将他翻转回来,这就和 \(dp_{i + 1}\) 相关。

可以列出式子 \(dp_i = \frac{i}{n} + \frac{n - i}{n}(dp_i + dp_{i + 1} + 1)\),化简可得:

\[dp_i = \frac{n}{i} + \frac{n - i}{i}dp_{i + 1} \]

设初始状态未匹配的个数为 \(x\),我们只需求出 \(\sum\limits_{i = 1}^{x}dp_i\) 即可。

参考代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;

void solve()
{
    int n, res = 0;
    string s1, s2;
    cin >> n;
    cin >> s1 >> s2;
    for (int i = 0; i < n; i ++ ) res += (s1[i] != s2[i]);
    vector<ll> inv(n + 1);
    inv[1] = 1;
    for (int i = 2; i <= n; i ++ ) inv[i] = (mod - mod / i * inv[mod % i] % mod) % mod;
    vector<ll> dp(n + 2);
    for (int i = n; i; i -- ) dp[i] = ((n - i) * dp[i + 1] + n) % mod * inv[i] % mod;
    ll ans = 0;
    for (int i = 1; i <= res; i ++ ) (ans += dp[i]) %= mod;
    cout << ans << "\n";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T = 1;
    cin >> T;
    while (T -- ) solve();
    return 0;
}
posted @ 2024-12-03 17:18  YipChip  阅读(5)  评论(0编辑  收藏  举报