【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[...][...]=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;
}
posted @ 2020-09-02 20:43  童年の波鞋  阅读(180)  评论(0编辑  收藏  举报