题意:
给出n个数字,选出若干个数字,使得这些数字的乘积是一个完全平方数,问有多少种选法。
解法:
考虑异或方程组,$x_i$表示第i个数字是否选,
注意到只要保证结果中各个质因数都出现偶数次就可保证结果是一个完全平方数。
相当于每个因数出现的次数$mod 2 = 0$。
这样对于每一个质因子,我们可以得到一个有n个变量的异或方程。
求矩阵中自由元的个数$cnt$,答案就是$2^{cnt}-1$
注意本题中变量数可能远大于方程数,我们不能普通地Jordan。
只要消成阶梯矩阵,并求出矩阵的秩即可。
用$bitmask$压位,可以做到$O(frac{n^3}{64})$
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <bitset> 5 6 #define N 2010 7 #define LL long long 8 #define P 1000000007LL 9 10 using namespace std; 11 12 bitset<N> g[N]; 13 bool v[N]; 14 int tot,prime[N]; 15 LL a[N]; 16 17 LL solve(int n,int m) 18 { 19 LL ans=1LL; 20 int k=1; 21 for(int i=1;i<=m;i++) 22 { 23 int t=0; 24 for(int j=k;j<=n;j++) 25 if(g[j][i]) 26 { 27 t=j; 28 break; 29 } 30 if(!t) 31 { 32 ans=ans*2LL%P; 33 continue; 34 } 35 swap(g[t],g[k]); 36 for(int j=k+1;j<=n;j++) 37 if(j!=k && g[j][i]) 38 g[j]^=g[k]; 39 k++; 40 } 41 return (ans-1LL+P)%P; 42 } 43 44 int main() 45 { 46 for(int i=2;i<=2000;i++) 47 { 48 if(v[i]) continue; 49 prime[++tot]=i; 50 for(int j=i+i;j<=2000;j+=i) 51 v[j]=1; 52 } 53 int T,n,Te=0; 54 cin>>T; 55 while(T--) 56 { 57 cin>>n; 58 for(int i=1;i<=tot;i++) g[i].reset(); 59 for(int i=1;i<=n;i++) 60 { 61 cin>>a[i]; 62 for(int j=1;j<=tot;j++) 63 { 64 LL tmp=a[i]; 65 while(tmp%prime[j]==0) 66 { 67 g[j][i]=g[j][i]^1; 68 tmp/=prime[j]; 69 } 70 } 71 } 72 printf("Case #%d: ",++Te); 73 cout << solve(tot,n) << endl; 74 } 75 return 0; 76 }