题目
题目链接
题解
选出来的数相乘要能组成一个完全平方数,则这个完全平方数进行素数分解以后,相同的素因子的个数是偶数个。
基于这个结论,我们对与每一个候选数a[i]a[i]进行质因数分解(最多有303个不同的质因子)。
然后针对每一个质因子pp,列一个包含所有个数的异或方程,如果某个数具有奇数个pp,那么这个数前的系数就应该为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;
}