AtCoder Regular Contest 116D I Wanna Win The Game

原题链接
知识点:DP, 二进制

将N个数看成二进制数,第i位看成2^(i - 1)
1.总和不大于M <== > N个二进制数中为1的权值的和 <= m
2.异或和为0 <==> 每一位都有偶数个1
f[i][j]:表示N个数的二进制的后i位的和

代码:

#include <iostream>
#include <cstring>

using namespace std;

typedef long long LL;

const int N = 5010, M = 15, mod = 998244353;

int p[M], n, m;
LL f[M][N], fact[N], infact[N];


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

LL C(int a, int b) {
    if (a < b || a < 0 || b < 0)    return 0;
    return fact[a] * infact[b] % mod * infact[a - b] % mod;
}

int main() {
    p[0] = f[0][0] = 1;
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i ++) {
        fact[i] = fact[i - 1] * i % mod;
        infact[i] = infact[i - 1] * qmi(i, mod - 2) % mod;
    }
    for (int i = 1; i < M; i ++) {
        p[i] = p[i - 1] * 2 % mod;
    }

    cin >> n >> m;
    for (int i = 1; i < M; i ++) {
        for (int j = 0; j <= m; j ++) {
            int t = p[i - 1];
            f[i][j] = f[i - 1][j];
            for (int k = 2; j >= k * t && k <= n; k += 2) {
                f[i][j] = (f[i][j] + f[i - 1][j - k * t] * C(n, k) % mod) % mod;
            }
        }
    }
    cout << f[M - 1][m] << endl;
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值