高斯消元解异或方程。
题意:
给你n个数,然后让你选择一些数,乘起来成为完全平方数,问你有多少种方案。
分析:
例如四个整数4,6,10,15。他们的素因子只有2,3,5这3种,把他们写成01向量的形式4 = 2^2 * 3^0 * 5 ^ 0 ->(2,0,0);
6 = 2^1 * 3^1 * 5 ^0 ->(1,1,0), 10 = 2^1 * 3^0 * 5^1 ->(1,0,1), 15 = 2^0 * 3^1 * 5^1 ->(0,1,1).
选出来的数乘积为2^(2*x1 + x2 + x3) * 3^(x2 + x4) * 5^(x3 + x4); 要使这个数为平方数,每个幂都为偶数,
即
x2+x3 = 0(mod2)
x2+x4 = 0(mod2)
x3+x4 = 0(mod2)
或
x2 xor x3 = 0
x2 xor x4 = 0
x3 xor x4 = 0
就变成了一个求线性方程组的秩, 最后减一, 因为题意不允许一个都不选,即0 0 0 0这组不成立。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define MAX 2000 + 10
#define MOD 1000000007
using namespace std;
long long c[MAX][505], p[MAX], v[MAX], a[MAX], cnt, n;//c矩阵,p素数表,cnt素数的个数
void mprime()//打素数表
{
int i,j;
memset(v,0,sizeof(v));
memset(p,0,sizeof(p));
cnt=0;
for(i=2; i<=2000; i++)
{
if(!v[i])
{
p[++cnt]=i;
for(j=i; j<=2000; j+=i)
{
v[j]=1;
}
}
}
}
int Rank()//计算秩
{
int i,j,k,r,u;
i=0;
j=0;
while(i<=cnt && j<=n)
{
r=i;
while(!c[r][j] && r<=cnt)
r++;
if(c[r][j])
{
swap(c[i],c[r]);//如果发现了第r行第j列为1,就讲r行和i行行互换(初等行变换)
for(u=i+1; u<=cnt; u++)
{
if(c[u][j])
{
for(k=i; k<=n; k++)
{
c[u][k]=c[u][k]^c[i][k];//每找到一个未知数就对其进行亦或处理,去掉系数c[i][k]
}
}
}
i++;
}
j++;
}
return i;
}
int main()
{
mprime();
int cns = 1, T;
scanf("%d", &T);
while(T--)
{
scanf("%lld", &n);
memset(c, 0, sizeof(c));
for(int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= cnt; j++)
{
long long num = a[i];
if(num%p[j] == 0)
{
while(num%p[j] == 0)
{
num /= p[j];
c[j][i] = c[j][i]^1;
}
}
}
}
long long k = (n-Rank());
long long ans = 1;
for(int i = 1; i <= k; i++)
ans = (ans*2) % MOD;
printf("Case #%d:\n", cns++);
printf("%lld\n", ans-1);
}
return 0;
}