C. Square Subsets 题解(状压dp)
题目链接
题目思路
首先呢,状压\(dp\)还是可以想到的
发现只有20个质数,设\(dp[i][j]\)表示前\(i\)个数状态为\(j\)的方案数
但是时间复杂度明显不允许这样复杂为\(n*2^{20}\)
考虑优化
发现数字非常少只有70个,而且对于最后状态的影响,大小为\(i\)的数的个数只和奇偶有关
那么我们设\(dp[i][j]\)表示选取了大小\(1-i\)的数中状态为\(j\)的方案数
注意\(c(n,0)+c(n,2)+c(n,4)...=c(n,1)+c(n,3)+c(n,5)...=2^{n-1}\)
代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define pii pair<long long,long long >
//typedef pair<long long,long long > pii
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
const ll INF=0x3f3f3f3f3f3f3f3f;
int n;
int a[maxn];
int isprime[80];
int tot;
int msk[80];
ll pw[maxn];
int cnt[80];
int dp[71][1<<19];
bool check(int x){
for(int i=2;i*i<=x;i++){
if(x%i==0) return 0;
}
return 1;
}
void init(){
for(int i=2;i<=70;i++){
if(check(i)){
isprime[i]=tot++;
}
}
for(int i=1;i<=70;i++){
int now=i;
for(int j=2;j*j<=now;j++){
if(now%j==0){
int tot=0;
while(now%j==0){
tot++;
now/=j;
}
if(tot%2){
msk[i]|=(1<<isprime[j]);
}
}
}
if(now!=1){
msk[i]|=(1<<isprime[now]);
}
}
}
signed main(){
init();
scanf("%d",&n);
pw[0]=1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
pw[i]=pw[i-1]*2%mod;
cnt[a[i]]++;
}
dp[0][0]=1;
for(int i=1;i<=70;i++){
for(int j=0;j<(1<<tot);j++){
if(cnt[i]==0){
dp[i][j]+=dp[i-1][j];
}else{
dp[i][j]+=dp[i-1][j]*pw[cnt[i]-1]%mod;
dp[i][j]+=dp[i-1][j^msk[i]]*pw[cnt[i]-1]%mod;
}
dp[i][j]%=mod;
}
}
printf("%d\n",(dp[70][0]-1+mod)%mod);
return 0;
}
不摆烂了,写题