参考自:http://www.cnblogs.com/flipped/p/5771492.html
自己做的时候不知道如何求种数。看了题解,感觉思路灰常巧妙。同时也感觉这是一道好题。
精髓在于转化为线性方程组。
求素数的思想,和高斯消元需要多加熟悉。
300个最大质因数小于2000的数,选若干个它们的乘积为完全平方数有多少种方案。
合法方案的每个数的质因数的个数的奇偶值异或起来为0。
比如12=2^2*3,对应的奇偶值为01(2的个数是偶数为0,3的个数是奇数为1),3的对应奇偶值为01,于是12*3是完全平方数。
然后异或方程组就是:
a11x1+a12x2+...+a1nxn=0
a21x1+a22x2+...+a2nxn=0
...
an1x1+an2x2+...+annxn=0
aij:第i个质数(2000内有303个质数)在第j个数里是奇数个则为1,否则为0。
xi:第i个数(最多300个数)被选则为1,否则为0。
求出有多少种解即可。(异或方程组高斯消元求秩,然后解就有2^(n-rank)种,减去全为0的解)
#include<iostream> #include<cstring> #include<algorithm> #include<cstdio> using namespace std; #define LL long long #define mod 1000000007 const int N=2000; const int M=310; int prime[N+2],cnt; int n,t,mat[M][M]; LL a[M]; void getPrime() //求2000以内的所有质数 { for(int i=2; i<=N; i++) { if(!prime[i]) prime[++cnt]=i; for(int j=1; j<=cnt&&prime[j]<=N/i; j++) { prime[prime[j]*i]=1; if(i%prime[j]==0) break; } } } int Rank(int c[][M]) //高斯消元求方程组的秩(线性表换将矩阵转化为上阶梯形矩阵) { int i=0,j=0,k,r,u; while(i<=cnt&&j<=n) { r=i; while(c[r][j]==0&&r<=cnt) r++; if(c[r][j]) { swap(c[i],c[r]); for(u=i+1; u<=cnt; u++) if(c[u][j]) for(k=i; k<=n; k++) c[u][k]^=c[i][k]; i++; } j++; } return i; } int solve() { memset(mat,0,sizeof(mat)); for(int i=1; i<=n; i++) for(int j=1; j<=cnt; j++) { LL tmp=a[i]; while(tmp%prime[j]==0) { tmp/=prime[j]; mat[j][i]^=1; } } int b=n-Rank(mat); LL ans=1,k=2; while(b) //快速幂 { if(b&1) ans=ans*k%mod; k=k*k%mod; b>>=1; } return ans-1; } int main() { int cas=1; getPrime(); scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%I64d",&a[i]);//cout<<"*"; printf("Case #%d:\n%d\n",cas++,solve()); } }
时间: 2024-10-21 19:10:28