题意:给出n个数 要求选出若干个数,求使得乘积为平方数的方法数
已知每个数的素因子不超过2e3 最后的乘积可以看作,所有素因子幂次方的乘积
因为乘积为平方数,所以每个素因子的幂都应该mod2=0
素因子的幂由每个数是否选决定 设x为是否选该数,x取(0|1)
所以能得到maxp+1个方程,偶数贡献为0,每个变量系数取0,1 mod2=0等价于xor=0方程
因为是齐次方程 求出秩即非自由变量个数 剩下n-r个自由变量只能取0,1有2^(n-r)-1种选法 (至少选一个,不能全为0)
例子 4,6,10,15
素因子 2^(2x1+x2+x3) 要求系数(2x1+x2+x3)mod2==0->化解(x2+x3)mod2=0 ->x2 XOR x3 =0
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=3e3+20;
int pri[N],vis[N],pn;
int a[N][N];//系数矩阵
void gen_prime(int n)
{
pn=0;
memset(vis,-1,sizeof(vis));
for(int i=2;i<=n;i++)
{
if(vis[i]==-1)
{
pri[pn++]=i;
for(int j=i+i;j<=n;j+=i)
{
vis[j]=0;
}
}
}
}
int Rank(int m,int n)
{
int i=0,j=0,k,r,u;
while(i<m&&j<n)//第i个方程,第j个变量
{
r=i;
for(k=i;k<m;k++)
{
if(a[k][j])//第i行第一个非0为第j列
{
r=k;
break;
}
}
if(a[r][j])
{
if(r!=i)
{
for(k=0;k<n;k++)
swap(a[r][k],a[i][k]);
}
//消元后第i行第一个非0为第j列,且u>i的第j列均为0
for(u=i+1;u<m;u++)
{
if(a[u][j])
{
for(k=i;k<n;k++)
{
a[u][k]^=a[i][k];
}
}
}
i++;//非0行个数
}
j++;
}
return i;
}
int main()
{
gen_prime(2100);//2e3内的素数
int t;
cin>>t;
for(int cas=1;cas<=t;cas++)
{
int n;
int maxp=0;//用了多少个素数,即系数矩阵的row
cin>>n;
memset(a,0,sizeof(a));
for(int i=0;i<n;i++)
{
ll x;
cin>>x;
for(int j=0;j<pn;j++)
{
while(x%pri[j]==0)
{
maxp=max(maxp,j);
x/=pri[j];
a[j][i]^=1;//更新第j个方程的第i列
}
}
}
int r=Rank(maxp+1,n);//row,col
ll ans=1;
for(int i=0;i<n-r;i++){
ans*=2;
ans%=mod;
}
printf("Case #%d:\n%lld\n",cas,ans-1);
}
return 0;
}