题目大意:从n个数a1,a2,…,an里选出若干个数(至少选一个),然后把选出的数乘起来得到b,问b是完全平方数的取数方法有多少种。每个数的质因子不超过2000,答案模上1000000007。
这道题目和POJ1830开关问题类似。首先,n个数中每个数都有取与不取两种操作,令向量
x⃗ T=(x1,x2,⋯,xn)
,其中
xi
表示是否取第
i
个数,
题目中说质因子的大小不会超过2000,分解质因子可行,对每个数分解质因子即可得到矩阵
F
。由于我们只关心质因子指数的奇偶性,可将每个元素都模2(或者说将原始方程组里的
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef unsigned long long ull;
const int maxn = 2001, mod = 1000000007;
bool isnpri[maxn];
int prime[maxn], k;
inline void getPrime()
{
for (int i = 2; i < maxn; i++)
{
if (!isnpri[i]) prime[k++] = i;
for (int j = 0; j < k && i*prime[j] < maxn; j++)
{
isnpri[i*prime[j]] = true;
if (i%prime[j] == 0) break;
}
}
}
ull a[300];
int fac[maxn][300];
inline void factor(int n)
{
ull now = a[n];
for (int i = 0; i < k; i++)
while (now%prime[i] == 0)
fac[i][n] ^= 1, now /= prime[i];
}
inline int gauss(int n)
{
int i = 0, j = 0;
while (j < n)
{
int id = i;
for (int k = i; k < ::k; k++)
if (abs(fac[k][j])) id = k;
if (id != i)
{
for (int k = j; k < n; k++)
swap(fac[i][k], fac[id][k]);
}
if (fac[i][j] == 0) { j++; continue; }
for (int k = i + 1; k < ::k; k++)
{
if (fac[k][j] == 0) continue;
for (int l = j; l < n; l++)
fac[k][l] ^= fac[i][l];
}
i++, j++;
}
return n - i;
}
inline ull pow_mod(ull x, ull n)
{
ull ret = 1;
while (n)
{
if (n & 1)
ret = ret*x%mod;
x = x*x%mod;
n >>= 1;
}
return ret;
}
int main()
{
getPrime(); //筛出2000内的素数
int T, t = 0;
cin >> T;
while (T--)
{
memset(fac, 0, sizeof(fac));
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
factor(i); //分解质因数
}
int free_element = gauss(n);
ull ans = pow_mod(2, free_element) - 1;
cout << "Case #" << ++t << ":\n";
cout << ans << endl;
}
return 0;
}