C. Square Subsets
題目鏈接
給一個序列ai,長度n,問有多少種方法可以選一些數字出來,且使得這些數字乘積是一個平方數。
n<=1e5 , ai<=70
任意一個平方數可以表示爲p1^a1 * p2^a2 * ……其中p1表示質數,a1是一個偶數,且我們不關心具體是多少隻需要知道是奇偶即可,那麼可以直接用01來表示。
觀察到ai比較小,我們直接把70以內的數字拆分成質因子表示形式,然後用二進制來表示它的冪,然後狀壓計數即可。
#include<bits/stdc++.h>
using namespace std;
#define LONG long long
#define clr0(x) memset(x , 0 , sizeof x)
const LONG MOD = 1e9 + 7 ;
int take[77] ;
int pw[77] ;
int ss[77] ;
LONG POW[100100] ;
int f[73][(1<<19 ) +100] ;
LONG Qpow(LONG a , int b )
{
return POW[b] ;
}
int main()
{
POW[0] = 1 ;
for(int i = 1 ;i <= 100000 ; ++ i)
POW[i] = POW[i-1] * 2 %MOD ;
int T = -1 ;
for(int i = 2 ; i <= 70 ; ++ i)
{
int judge = 0 ;
for(int j = 2;j < i ; ++ j)
if(i %j ==0 ) judge = 1;
if(!judge )
{
T ++ ;pw[i] = (1<< T ) ;
}
}
for(int i = 2 ; i <= 70 ; ++ i)
{
int tmp= i ;ss[i] = 0 ;
for(int j = 2;tmp > 1;)
{
if(tmp % j == 0)
{
tmp /= j ;
ss[i] ^= pw[j] ;
j = 2;
}
else j ++ ;
}
}
clr0(take) ;
int n , a ;
scanf("%d",&n) ;
int tot =0 ;
for(int i = 1; i<= n ; ++ i)
scanf("%d",&a) , take[a] ++ ;
clr0(f) ;
f[0][0] = 1;int p = 0;
for(int i = 2 ; i <= 70 ; ++ i)
{
if(take[i] <= 0 ) continue ;
p ++ ;
for(int j = 0 ; j < (1 <<19 ) ; ++ j )
{
f[p][j] += (int ) ( Qpow( 2, take[i] - 1 ) * f[p-1][j] % MOD );
f[p][j^ss[i]] += (int )( Qpow( 2 , take[i] -1 ) * f[p-1][j ] % MOD ) ;
}
}
printf("%lld\n" , Qpow(2 , take[1] ) * f[p][0] % MOD - 1 ) ;
}