Problem - 1829H - Codeforces
很遗憾,这道题目的出题人没有想到一个有趣的故事,所以他只是让你解决以下问题。
给定由n个正整数组成的数组a,计算具有子序列中元素的按位AND在其二进制表示中恰好有k个设置位的非空子序列的数量。答案可能很大,因此请将其模除109+7输出。
回顾一下,数组a的子序列是可以通过从a中删除一些(可能为零)元素获得的序列。例如,[1,2,3]、[3]和[1,3]是[1,2,3]的子序列,但[3,2]和[4,5,6]不是。
请注意,AND 表示按位 AND 操作。 输入
每个测试用例包含多个测试用例。第一行包含测试用例数量t (1≤t≤104)。接下来是每个测试用例的描述。
每个测试用例的第一行包括两个整数n和k (1≤n≤2⋅105,0≤k≤6)——数组的长度和带有按位AND计数子序列应该在其二进制表示中具有的设置位数。
每个测试用例的第二行包含n个整数ai (0≤ai≤63)——数组a。
保证所有测试用例中n的总和不超过2⋅105。 输出
对于每个测试用例,输出一个整数——具有恰好k个设置位的按位AND值的子序列的数量。答案可能很大,因此请将其模除109+7。
Example
Input
Copy
6
5 1
1 1 1 1 1
4 0
0 1 2 3
5 1
5 5 7 4 2
1 2
3
12 0
0 2 0 2 0 2 0 2 0 2 0 2
10 6
63 0 63 5 5 63 63 4 12 13
Output
Copy
31 10 10 1 4032 15
题解:
简单dp
dp[i][j]代表目前选到第i个结果为j的情况有多少种
1.第i个不选
dp[i][j] = dp[i][j] + dp[i - 1][j]
2.第i个选
dp[i][j&a[i]] = dp[i][j&a[i]] + dp[i - 1][j]
第i个选了之后就变成了,j&a[i]由前面j转移过来
注意初始化
每一个ai都可以作为一个起点
dp[i][a[i]] = 1;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include<iostream>
#include<vector>
#include<set>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
#define int long long
typedef pair<int,int> PII;
typedef unsigned long long ULL;
const int N = 4e5 + 10;
int mod = 1e9 + 7;
int dp[N][64];
int a[N];
void solve()
{
int n,k;
cin >> n >> k;
for(int i = 1;i <= n;i++)
{
cin >> a[i];
}
for(int i = 1;i <= n;i++)
{
dp[i][a[i]] = 1;
for(int j = 0;j <= 63;j++)
{
dp[i][j] = (dp[i][j] + dp[i - 1][j])%mod;
dp[i][j&a[i]] = (dp[i][j&a[i]] + dp[i - 1][j])%mod;
}
}
int ans = 0;
for(int i = 0;i <= 63;i++)
{
int cnt = 0;
for(int j = 0;j < 6;j++)
{
if((i >> j)&1)
{
cnt ++;
}
}
if(cnt == k)
{
ans = (ans + dp[n][i])%mod;
}
}
cout << ans <<"\n";
for(int i = 1;i <= n;i++)
{
for(int j = 0;j <= 63;j++)
{
dp[i][j] = 0;
}
}
}
signed main()
{
ios::sync_with_stdio(0 );
cin.tie(0);cout.tie(0);
int t = 1;
cin >> t;
while(t--)
{
solve();
}
}