cf1778 D. Flexible String Revisit(概率dp+数学)

传送门

#include <bits/stdc++.h>

using namespace std;

#define endl "\n"
#define debug cout<<"debug"<<endl
typedef long long ll;
typedef long double ld;
typedef pair<int, int> PII;
const double eps = 1e-8;
const double PI = 3.14159265358979323;
const int N = 2e5+10, M = 2*N, mod = 998244353;
const int INF = 0x3f3f3f3f;

PII operator - (PII a, PII b)
{
    return {(a.first - b.first + mod)%mod, (a.second - b.second + mod)%mod};
}

PII operator - (PII a, ll b)
{
    return {a.first,(a.second - b + mod)%mod};
}

PII operator * (PII a, ll b)
{
    return {(a.first*b%mod+mod)%mod,(a.second*b%mod+mod)%mod};
}

PII operator % (PII a, ll mod)
{
    return {(a.first%mod + mod)%mod, (a.second % mod + mod)%mod};
}

ll qmi(int a, int k)
{
    ll res = 1;
    while(k)
    {
        if(k&1) res = (ll)res * a % mod;
        k>>=1;
        a = (ll)a*a % mod;
    }
    return res;
}

// dp[i] 表示有i个不同位, 操作的期望
// 操作结果只会使不同位+1/-1, 若-1 则概率i/n, +1, 概率为 1-i/n
// 所以得dp[i] = 1 + dp[i-1]*(i/n) + dp[i+1]*(1-i/n)
// ==>  dp[i-1] = 1 + dp[i-2]*((i-1)/n) + dp[i]*(1 - (i-1)/n)
// ==>  dp[i] = {dp[i-1] - 1 - dp[i-2]*((i-1)/n)}*(1/(1 - (i-1)/n))
// 初始 dp[0] = 0, dp[n] = 1 + dp[n-1]
// 因为求解所有答案需要两个状态来转移, 而已知只有一个dp[0], 所以只能设dp[1], 通过dp[n] = 1 + dp[n-1]来递推求方程解得dp[1]
// 通过递推, 不难发现dp[i]满足, dp[i] = a*dp[1] + b
// 然后推出dp[n] dp[n-1] 联立即可得到dp[1]
// 最后通过dp[0] dp[1] 来获得所以答案
// 为了方便计算, 设dp[i] = {a, b}, 即dp[i] = a*dp[1] + b;

ll n;
string a, b;

void solve()
{
    cin>>n;
    cin>>a>>b;
    int cnt = 0;
    for(int i = 0; i<n; i++) cnt += (a[i]!=b[i]);
    PII dp[n+1];
    dp[0] = {0, 0};
    dp[1] = {1, 0};
    // dp[i] = {dp[i-1] - 1 - dp[i-2]*((i-1)/n)}*(1/(1 - (i-1)/n)) 递推求dp[n] 和 dp[n-1] 的系数
    ll nn = qmi(n, mod-2)%mod;
    for(int i = 2; i<=n; i++)
    {
        auto t = (1 - (i-1)*nn%mod + mod)%mod;
        dp[i] = (dp[i-1] - 1 - dp[i-2]*(i-1)%mod*nn%mod)*qmi(t, mod-2)%mod;
    }
    // dp[n] = 1 + dp[n-1]
    // 1 = dp[n] - d[n-1] = a*dp[1] + b;
    // dp[1] = (1-b)/a
    auto tmp = dp[n] - dp[n-1];
    auto d1 = (1-tmp.second)*qmi(tmp.first, mod-2)%mod;
    cout<<(dp[cnt].first*d1%mod + dp[cnt].second + mod) % mod<<endl;
}

int main()
{
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int T;
    T = 1;
    cin>>T;
    while(T -- )
    {
        solve();
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值