【01背包】B001_AW_异或和是质数的子集数(上限分析+滚动数组优化内存)
给出 n 个互不相同的正整数。
问存在多少个子集,使得子集中所有数的异或和是质数。
由于答案可能很大,请你输出对 10^9+7 取模后的结果。
输入格式
第一行包含整数 n。
第二行包含 n 个正整数。
输出格式
输出一个整数,表示满足条件的子集数量对 109+7 取模后的结果。
数据范围
1≤n≤5000,
1≤ 给定正整数 ≤5000。
输入样例:
3
1 2 3
输出样例:
4
方法一:dp
子集是无重复元素的,故可看成 01 背包
- 定义状态:
- f[i][j] 表示从前 i 个数中选出若干个数,异或和 j 的方案数
- tip:由于异或是不进位的加法,故 n 个数异或的结果不会超过最大数,最大数为 5000,比 \(2^{12}\) 大一点(\(12=log_{k}5000/log_{k}2\)),但我不太清楚 5000 的二进制最高位的 1 在第几位,为了保险这里第二维直接取 \(2^{13}\);
- f[i][j] 表示从前 i 个数中选出若干个数,异或和 j 的方案数
- 思考初始化:
- f[...][...]=0,f[0][0]=1
- 思考状态转移方程:
- f[i][j]=f[i-1][j]
- f[i][j]=f[i][j]+f[i-1][j^A[i]],if (j^A[i] < M)
- 思考输出:f[n][j],j∈[2,M],if (isPrime(j))
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7, M=pow(2, 13)+5;
bool st[M];
void inti() {
memset(st, true, sizeof st);
for (int i=2; i<M; i++) if (st[i])
for (int j=i*i; j<M; j+=i)
st[j]=false;
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int n; cin>>n; inti();
int A[n+1], f[2][M];
for (int i=1; i<=n; i++) cin>>A[i];
memset(f, 0, sizeof f); f[0][0]=1;
int k=1;
for (int i=1; i<=n; i++) {
for (int j=0; j<M; j++) {
f[k][j]=f[k^1][j]; int nx=A[i]^j;
if (nx<M)
f[k][j]=(f[k][j]+f[k^1][nx])%mod;
}
k^=1;
}
int ans=0;
for (int j=2; j<M; j++) if (st[j])
ans=(ans+f[k^1][j])%mod;
cout << ans;
return 0;
}