HDU 6391 组合数学 + DP

157 篇文章 1 订阅
题意

传送门 HDU 6391 Lord Li’s problem

题解

仅考虑 S i ≠ T i S_i\neq T_i Si=Ti 的数量 m m m,最后答案除以 ( n m ) \binom{n}{m} (mn) 即可。考虑 X X X 的排列,最后答案除以 k ! k! k! 即可。

d p [ i + 1 ] [ j ] dp[i+1][j] dp[i+1][j] 代表考虑 X 0 ⋯ X i X_0\cdots X_i X0Xi,这些数字异或和中 1 的数量为 j j j 情况下方案的数量。令 a , b a,b a,b 分别为将 X i X_i Xi 异或进来后,异或和为 0 和 1 的数量,对应的贡献为 d p [ i ] [ j ] ⋅ ( j a ) ⋅ ( n − j b ) dp[i][j]\cdot\binom{j}{a}\cdot\binom{n-j}{b} dp[i][j](aj)(bnj) X i X_i Xi 可以为任意数字,那么要从 d p [ i + 1 ] [ j ] dp[i+1][j] dp[i+1][j] 中减去 X i X_i Xi 之前出现过的情况,对应的贡献为 d p [ i − 1 ] [ j ] ⋅ i ⋅ [ ( n 3 ) − ( i − 1 ) ] dp[i-1][j]\cdot i\cdot[\binom{n}{3}-(i-1)] dp[i1][j]i[(3n)(i1)]。单个样例时间复杂度 O ( n k ) O(nk) O(nk)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int MOD = 19260817;
constexpr int N = 42;
ll fac[N], inv[N], invf[N];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    fac[0] = invf[0] = 1;
    fac[1] = inv[1] = invf[1] = 1;
    for (int i = 2; i < N; ++i) {
        fac[i] = fac[i - 1] * i % MOD;
        inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
        invf[i] = invf[i - 1] * inv[i] % MOD;
    }

    auto get = [&](int n, int m) -> ll {
        if (n < 0 || m < 0 || n < m) {
            return 0;
        }
        return fac[n] * invf[m] % MOD * invf[n - m] % MOD;
    };

    auto power = [&](ll x, int n) -> ll {
        ll res = 1;
        while (n > 0) {
            if (n & 1) {
                (res *= x) %= MOD;
            }
            (x *= x) %= MOD, n >>= 1;
        }
        return res;
    };

    int n, k, tt = 0;
    while (cin >> n >> k) {
        tt += 1;
        if (n == 0 && k == 0) {
            break;
        }
        string s, t;
        cin >> s >> t;
        int m = 0;
        for (int i = 0; i < n; ++i) {
            m += s[i] != t[i];
        }
        vector<vector<ll>> dp(k + 1, vector<ll>(n + 1));
        dp[0][0] = 1;
        for (int i = 0; i < k; ++i) {
            for (int j = 0; j <= n; ++j) {
                for (int a = 0; a <= 3; ++a) {
                    int b = 3 - a;
                    int nxt = j + (b - a);
                    if (0 <= nxt && nxt <= n) {
                        (dp[i + 1][nxt] += dp[i][j] * get(j, a) % MOD * get(n - j, b) % MOD) %= MOD;
                    }
                }
            }
            if (i - 1 >= 0) {
                for (int j = 0; j <= n; ++j) {
                    dp[i + 1][j] -= dp[i - 1][j] * i % MOD * (get(n, 3) - (i - 1)) % MOD;
                    (dp[i + 1][j] += MOD) %= MOD;
                }
            }
        }
        ll res = dp[k][m];
        (res *= invf[k]) %= MOD;
        (res *= power(get(n, m), MOD - 2)) %= MOD;
        cout << "Case #" << tt << ": " << res << '\n';
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值