A Bit More Common (2024牛客多校1 B)


进入博客阅读体验更佳:A Bit More Common (2024牛客多校1 B) | 付诺の小站 (funuo0728.github.io)

A Bit More Common

题目大意


给定两个整数 n n n m ( 1 ≤ n , m ≤ 5000 ) m(1 \le n,m \le 5000) m(1n,m5000),在所有长度为 n n n且元素取值范围为 [ 0 , 2 m ) [0, 2 ^ m) [0,2m)的序列中,计算存在两个合法子序列的序列个数。其中,合法子序列是指子序列中所有元素按位与的结果为 1 1 1

由于答案可能很大,请将其对 q ( 1 ≤ q ≤ 1 0 9 ) q(1 \le q \le 10^9) q(1q109)取模。

解题思路


首先考虑至少存在一个合法子序列的序列个数,对于合法子序列中的元素,第 0 0 0位必须全为 1 1 1,其余位上至少有一个 0 0 0,对于剩下的其他元素,第 0 0 0位必须全为 0 0 0,其余位任选。我们对序列长度进行枚举,最终得到:

∑ i = 1 n C n i ( 2 i − 1 ) m − 1 2 ( m − 1 ) ( n − i ) \sum_{i = 1}^{n}C_{n}^{i}(2 ^ {i} - 1)^{m - 1}2^{(m - 1)(n-i)} i=1nCni(2i1)m12(m1)(ni) ① ①

题目要求我们求至少存在两个合法子序列的序列个数,那么接下来只需要求恰好只有一个合法子序列的情况,最后用至少 1 1 1个的方案数减去恰好 1 1 1个的方案数即为所求结果。

考虑这样一种按位与为 1 1 1的子序列,去掉其中任何一个元素,他们的按位与都会变得不为 1 1 1。首先这些元素的第 0 0 0位上肯定都为 1 1 1,自然想到如果某一个数在某一位上为 0 0 0,而其余数在这一位上都为 1 1 1,那么这个数就是不可或缺的。姑且将这种有且只有一个数在这一位上为 0 0 0的位称为特殊位,只要每一个数都至少对应一位特殊位,这样得到的子序列就是要求的子序列。

考虑二维 d p dp dp f [ i ] [ j ] f[i][j] f[i][j]表示总共有 i i i个数, j j j位特殊位,每个数至少对应一个特殊位的方案数。

对于 f [ i ] [ j ] f[i][j] f[i][j]来说,每加入一位特殊位,可以使 i i i个数字中的其中一个对应它,共有 i i i种情况;也可以新增一个数字来对应它,然后发现从 f [ i ] [ j ] f[i][j] f[i][j] f [ i + 1 ] [ j + 1 ] f[i +1][j + 1] f[i+1][j+1]难以确定转移方程,于是思考从 f [ i + 1 ] [ j + 1 ] f[i + 1][j + 1] f[i+1][j+1] f [ i ] [ j ] f[i][j] f[i][j],发现去掉一个特殊位并使数减少的情况共有 i + 1 i + 1 i+1种,最终可得转移方程:

f [ i ] [ j ] = i ⋅ f [ i ] [ j − 1 ] + i ⋅ f [ i − 1 ] [ j − 1 ] f[i][j] = i\cdot f[i][j - 1] + i\cdot f[i - 1][j - 1] f[i][j]=if[i][j1]+if[i1][j1]

那么恰好存在一个合法子序列的思考方式与至少存在一个类似:

首先确定子序列的长度 i i i,取长度为 i i i的子序列的方案数为 C n i C_{n}^{i} Cni

0 0 0位全部为 1 1 1,每个数至少对应 1 1 1个特殊位,那么现在确定特殊位的个数 j j j,至少为 i i i,至多为 m − 1 m - 1 m1,取 j j j个特殊位的方案数为 C m − 1 j C_{m -1}^{j} Cm1j

i i i个数对应 j j j个特殊位的方案数位 f [ i ] [ j ] f[i][j] f[i][j]

对于除去第 0 0 0位和特殊位的其他位,每一位的总方案数为 2 i 2 ^ i 2i,减去全为 1 1 1的情况为 2 i − 1 2 ^ i -1 2i1,再减去使这一位成为特殊位的情况 2 i − i − 1 2 ^ i - i - 1 2ii1,最后这样的位有 m − 1 − j m - 1 - j m1j个,那么方案数为 ( 2 i − i − 1 ) m − 1 − j (2^i - i - 1) ^ {m-1-j} (2ii1)m1j

最后考虑其余数字,对于这些数字来说,除了第 0 0 0位,其余位任填,方案数位 2 ( m − 1 ) ( n − i ) 2 ^ {(m - 1)(n-i)} 2(m1)(ni)

最终方案数为:

$\sum_{i = 1}{n}2{(m - 1)(n - i)}C_ni\cdot\sum_{j=i}{m-1}C_{m-1}jf[i][j](2i - i - 1) ^ {m-1-j} $ ② ②

最后用 ① ① 式减去 ② ② 式即可。

参考代码

#include <bits/stdc++.h>
#define maxn 5010
#define int long long
using namespace std;
const double eps = 1e-8;
typedef long long ll;
typedef __int128_t i128;
i128 _base=1;

int n, m, mod, res = 0;
int C[maxn][maxn], f[maxn][maxn], pow2[maxn], tt[maxn];

inline ll mol(ll x){return x-mod*(_base*x>>64);}

int qmi(int a, int b, int m) {
    int res = 1;  a = mol(a);
    while(b) {
        if(b & 1)  res = mol(res * a);
        a = mol(a * a);
        b >>= 1;
    }
    return res;
}

void solve() {
    cin >> n >> m >> mod;

    _base=(_base<<64)/mod;

    pow2[0] = 1;
    for (int i = 1; i <= n; ++i)  pow2[i] = pow2[i - 1] * 2 % mod;
    for (int i = 1; i <= n; ++i)  tt[i] = qmi(2, (m - 1) * (n - i), mod);
 
    for (int i = 0; i <= 5000; ++i) {
        for (int j = 0; j <= i; ++j) {
            if(j == 0)  C[i][j] = 1;
            else  C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
        }
    }
 
    for (int i = 1; i <= n; ++i) {
        for (int j = i; j <= m - 1; ++j) {
            if(i == 1)  f[i][j] = 1;
            else  f[i][j] = i * (f[i][j - 1] + f[i - 1][j - 1]) % mod;
        }
    }
 
    for (int i = 1; i <= n; ++i) {
        int t1 = C[n][i] * qmi((pow2[i] + mod - 1) % mod, m - 1, mod) % mod * tt[i] % mod;
        res = (res + t1) % mod;
    }
 
    res = ((res - C[n][1] * qmi(2, (n - 1) * (m - 1), mod) % mod) % mod + mod) % mod;
 
    for (int i = 2; i <= n; ++i) {
        for (int j = i; j <= m - 1; ++j) {
            int t1 = C[n][i] * tt[i] % mod;
            int t2 = C[m - 1][j] * f[i][j] % mod * qmi(((pow2[i] - i - 1) % mod + mod) % mod, m - 1 - j, mod) % mod;
            res = ((res - (t1 * t2 % mod)) % mod + mod) % mod;
        }
    }

    cout << res << '\n';
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);  cout.tie(0);
    int t = 1;
    while (t--) {
        solve();
    }
    return 0;
}
  • 16
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值