(状压dp)
题意:给定一个集合,里面包括
n
思路:观察到
a[i]
的范围比
n
要小很多,于是我们可以用一个小数组保存:大小为
当
cnt[i]=0
时直接将
i−1
转移到
i
即可。
当
{dp[i][j]=dp[i][j]+dp[i−1][j]∗2cnt[i]−1dp[i][j⊕mask[i]]=dp[i][j⊕mask[i]]+dp[i−1][j]∗2cnt[i]−1
代码:
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
const int maxn = 100050;
const LL mod = 1e9 + 7;
LL dp[2][1<<19], pw[maxn];
int a[maxn], cnt[80], bitmask[80];
vector<int> prim;
void init() {
// get 2^n
pw[0] = 1;
for(int i=1; i<maxn; i++)
pw[i] = 2LL * pw[i-1] % mod;
prim.clear();
memset(cnt, 0, sizeof(cnt));
memset(bitmask, 0, sizeof(bitmask));
memset(dp, 0, sizeof(dp));
// get prime number
for(int i=2; i<71; i++) {
bool flag = 1;
for(int j=2; j*j<=i; j++)
if(i%j == 0) flag = 0;
if(flag) prim.push_back(i);
}
// get bitmask for each i
for(int i=2; i<71; i++) {
int t = i;
for(int j=0; j<(int)prim.size(); j++) {
int num = 0, div = prim[j];
while(t%div == 0) {
t /= div;
num ++;
}
if(num&1)
bitmask[i] += 1<<j;
}
}
}
int main() {
//freopen("test.txt","r",stdin);
init();
int n;
scanf("%d",&n);
for(int i=0; i<n; i++) {
scanf("%d",&a[i]);
cnt[a[i]] ++;
}
// solve
int sz = (int)prim.size();
dp[0][0] = 1;
for(int i=1; i<71; i++) {
int mask = bitmask[i], cur = i&1, last = !cur;
for(int j=0; j<(1<<sz); j++) {
if(cnt[i] == 0)
dp[cur][j] = dp[last][j];
else {
dp[cur][j] = (dp[cur][j] + dp[last][j]*pw[cnt[i]-1]%mod) % mod;
dp[cur][j^mask] = (dp[cur][j^mask] + dp[last][j]*pw[cnt[i]-1]%mod) % mod;
}
}
for(int j=0; j<(1<<sz); j++)
dp[last][j] = 0;
//printf("%d : %I64d\n",i,dp[i][0]);
}
printf("%I64d\n",(dp[0][0]-1+mod)%mod);
return 0;
}