题意:给出n个正整数,从中选出1个或者多个,使得选出来的整数乘积是完全平方数,一共有多少种选法。
思路:用01向量表示一个数,再用n个01向量来表示我们的选择,因为完全平方数要求素因子的次数一定要是偶数的,所以我们可以统计的将奇数当作1,偶数当作0,那么就是一组可以变换成oxr的方程组,最后的结果有自由变量f个,答案是2^f-1,f求解就是求n-方程组的秩,(本题不允许一个都不选,所以减去1,即去掉自由元全取0的这种情况)。
求的是方程解的个数,方程解的个数等于 2^自由变量的个数。
详解见白书 161页。
#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
#define bug puts("***********")
using namespace std;
const int N =510;
const LL mod =1000000007;
struct Mat{
int mat[N][N];
}sta;
int cnt=0;
int vis[2010];
int prime[N];
void init(){
mem(vis,0);
for(int i=2;i<2010;i++){
if(vis[i]==0){
prime[cnt++]=i;
for(int j=i*i;j<2010;j+=i)
vis[j]=1;
}
}
}
int ran(Mat a,int n,int m){
int i=0,j=0,r;
while(i<n&&j<m){ ///第i个方程 第j个变量。
int r=i;
for(int k=i;k<n;k++){
if(a.mat[k][j]){
r=k;
break;
}
}
if(a.mat[r][j]){
if(r!=i){
for(int k=0;k<=m;k++)swap(a.mat[r][k],a.mat[i][k]);
}
for(int k=i+1;k<n;k++){
if(a.mat[k][j]){
for(int g=0;g<=m;g++)
a.mat[k][g]^=a.mat[i][g];
}
}
i++;
}
j++;
}
return i;
}
int main(){
init();///cnt 个方程。
int t,n;
int maxn=0;
scanf("%d",&t);
LL x;
int cas=1;
while(t--){
mem(sta.mat,0);
scanf("%d",&n);
for(int i=0;i<n;i++){ /// n个变量
scanf("%lld",&x);
for(int j=0;j<cnt;j++){
while(x%prime[j]==0){
maxn=max(maxn,j);
x/=prime[j];
sta.mat[j][i]^=1;
}
}
}
int r=ran(sta,maxn+1,n);
LL num=1;
for(int i=1;i<=n-r;i++)
num=num*2%mod;
printf("Case #%d:\n%lld\n",cas++,(num+mod-1)%mod);
}
return 0;
}