1. 程式人生 > 其它 >C. Square Subsets 題解(狀壓dp)

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;
}

不擺爛了,寫題