题目
给定两个整数n和m,在所有包含n个小于2m的非负整数的序列中,需要统计存在A的非空子序列A的个数,其中整数的按位与为1.
注意,序列A的非空子序列是可以通过从A中删除零个或多个元素并将剩余元素按其原始顺序排列而获得的非空序列。
由于答案可能非常大,因此输出它以正整数 q 为模。
非负整数A和B的按位AND,A AND B定义如下:
• 当A AND B 以二为基数书写时,如果A 和B 都为1,则2d 位(d > 0) 的数字为1,否则为0。
例如,我们有 4 AND 6 = 4(以二为基数:100 AND 110 = 100)。
一般来说,k 个非负整数 P1, P2,…, Pk 的按位与定义为 (…((P1 AND p2) AND P3) AND
AND Pk) 并且我们可以证明
这个值
不依赖于顺序为 P1、P2、…、Pk。
唯一一行包含三个整数 n (1 ≤ n ≤ 5000)、m (1 ≤ m ≤ 5000) 和 q (1 ≤ q ≤ 109)。
输出一行包含一个整数,表示答案。
题目分析
分析
由题干我们可以推导出来下面结论:
- 满足条件的序列中一定含有奇数
- 我们只需要考虑一个序列A它存在满足条件的子序列,那么A可能有几种摸样,而不需要考虑A的子序列有几种
- 将数列以二进制数形式直观的展现,以便统计计算
思路
数列中含有n个数,这n个数字中可能有1~n个奇数,因此从这个角度思考的情况个数有
∑ i = 1 n ( n i ) \sum_{i=1}^{n}\binom{n}{i} i=1∑n(in)
在此基础上,数列中n个数的二进制最后一位的所有情况已经被考虑了。
因此接下来考虑剩下的m-1个位。
对于奇数来说,只要保证不会出现所有奇数的二进制数字在某一位上全是0的情况,这个数列就一定满足条件,这符合我们分析的第二条思想,只要序列有满足条件的子序列就够了。
因此所有奇数的m-1个位,不同奇数二进制数的同一个位都有两种可能:0/1;因此不同奇数二进制数的同一个位组合起来的情况数一共有 ( 2 i − 1 ) m − 1 (2^i-1)^{m-1} (2i−1)m−1个,PS:i是奇数的个数。
偶数同理,只是偶数不需要保证不会出现所有偶数数的二进制数字在某一位上全是0的情况,因此偶数情况数有: ( 2 n − i ) m − 1 (2^{n-i})^{m-1} (2n−i)m−1
综上一共情况数为 ∑ i = 1 n ( n i ) ∗ ( 2 i − 1 ) m − 1 ∗ ( 2 n − i ) m − 1 \sum_{i=1}^{n}\binom{n}{i}*(2^i-1)^{m-1}*(2^{n-i})^{m-1} i=1∑n(in)∗(2i−1)m−1∗(2n−i)m−1
解答
板子代码
ll Mod;
const ll N = 5e3 + 100;
ll comb[N][N];
auto setMod=[](ll n = 1e9+7){Mod = n;};
void get_comb(int n) {
for (int i = 0; i <= n; i++)
for (int j = 0; j <= i; j++)
comb[i][j] = (0 < j && j < i) ? (comb[i - 1][j - 1] + comb[i - 1][j]) % Mod : 1;
}
int C(int n, int m) {
if (n == m && m == -1) return 1; //* 隔板法特判
if (n < m || m < 0) return 0;
return comb[n][m];
}
/// 加法递推求组合数,O(n^2),模数非素数时可用
int qpow(int a, ll k) {
ll ans = 1;
while (k) {
if (k & 1) ans = 1LL * ans * a % Mod;
k >>= 1;
a = 1LL * a * a % Mod;
}
return ans;
}
/// 快速幂,O(logk),底数越大速度慢的越快
完整代码
Unimportant code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define For for (ll i = 1; i <= n; i++)
#define rFor for (ll i = n; i > 0; i--)
#define rep(i, sta, end) for (ll i = sta; i <= end; i++)
#define rrep(i, end, sta) for (ll i = end; i >= sta; i--)
#define All(x) for (auto item : x)
借上上面的板子
Implementation code
inline void solve() {
ll n,m,p;
cin>>n>>m>>p;
setMod(p);
get_comb(n+10);
//cout<<Mod<<endl;
ll ans=0;
For{
ans+=( C(n,i)%Mod * qpow( qpow( 2,i ) - 1 , m-1)%Mod * qpow( qpow( 2,n-i ),m-1 )%Mod)%Mod;
ans%=Mod;
}
cout<<ans<<endl;
}
int main() {
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int num = 1;
//cin>>num;
while (num--)
solve();
return 0;
}
代码重点分析(可无)
像这种遇到大数的代码题,如果存在int和 long long混用的情况容易出现一些很难发现的错误,而且自己测试除非对拍不然很难测试出来。
因此建议这种代码题全部改用long long 能改的全改