题目
题解
选出来的数相乘要能组成一个完全平方数,则这个完全平方数进行素数分解以后,相同的素因子的个数是偶数个。
基于这个结论,我们对与每一个候选数 a[i] a [ i ] 进行质因数分解(最多有303个不同的质因子)。
然后针对每一个质因子 p p ,列一个包含所有个数的异或方程,如果某个数具有奇数个 p p ,那么这个数前的系数就应该为1,否则应该为0。方程的右面始终为0,这是因为平方数的质因子个数为偶数。
我们可以列出多达303个异或方程组成的异或方程组,我们使用高斯消元算法来解这个异或方程组,假设解得自由元的个数为res个。
那么最终答案就是个。(要减去什么都不取的情况)。
总结
当发现这是一道涉及某个元素取或者不取的,并与xor或者奇偶相关的题目时,我们要思考一下异或方程组。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <bitset>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 307;
const int maxm = 307;
const ll mod = 1e9+7;
ll a[maxn];
bitset<maxn> ba[maxm];
int T,n,cas;
int primes[maxm];
int pc = 0;
int not_prime[2000];
int getprimes(){
for(int i = 2;i <= 2000;++i){
if(!not_prime[i]){
primes[pc++] = i;
for(int j = 2*i;j <= 2000;j += i)
not_prime[j] = 1;
}
}
}
bool nofree[maxn];
int xor_gauss(int n,int m){
/* 用于给定异或方程组消元
* 返回自由变元的个数 n-j
*/
memset(nofree,0,sizeof(nofree));
int j = 0;
for(int i = 0;i < n;++i){
int sp = j;
while(sp < m){
if(ba[sp][i]){
nofree[i] = true;
break;
}
sp++;
}
if(sp < m){
swap(ba[j],ba[sp]);
for(int k = j+1;k < m;++k){
if(ba[k][i]) ba[k] ^= ba[j];
}
++j;
}
}
//无解判定
for(int k = j;k < m;++k){
if(ba[k].count() == 1 && ba[k][n]) return -1;
}
return n-j;
}
int main(){
getprimes();
cin>>T;
while(T--){
for(int i = 0;i < maxm;++i) ba[i].reset();
cin>>n;
for(int i = 0;i < n;++i){
scanf("%lld",&a[i]);
}
for(int i = 0;i < pc;++i){
for(int j = 0;j < n;++j){
int cc = 0;
ll tmp = a[j];
while(tmp % primes[i] == 0){
cc ^= 1;
tmp /= primes[i];
}
ba[i][j] = cc;
}
}
int res = xor_gauss(n,pc);
ll ans = 1;
for(int i = 0;i < res;++i) ans = ans * 2 % mod;
printf("Case #%d:\n%lld\n",++cas,ans-1);
}
return 0;
}