C. Square Subsets 題解(狀壓dp)
阿新 • • 發佈:2021-11-10
題目連結
題目思路
首先呢,狀壓\(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; }