題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=5833
題目描述:給定n個數,每個數所含質因子最大不超過2000,選取任意個(至少爲1個)數字相乘,要求所得乘積爲完全平方數,求共有多少種選取方案。
思路:題目都已經說每個數所含最大質因子不超過2000了,很明顯是要分解質因子求解,求的2000以內的素數共303個。要想相乘組成完全平方數,只要所選取的x個數中含有的質因子都是偶數個都行,奇偶可以用10表示,1爲奇數個,0爲偶數個。首先需要求出每個數所含質因子及其個數,設a[i][j]表示第j個數所含質因子i的個數,0表示偶數個,1表示奇數個。x[i]表示是否選取第i個數。則只需:
a[1][1] *x[1] ^ a[1][2] * x[2] ^ ...... ^ a[1][n] * x[n] = 0
a[2][1] *x[1] ^ a[2][2] * x[2] ^ ...... ^ a[2][n] * x[n] = 0
...........
a[n][1] *x[1] ^ a[n][2] * x[2] ^ ...... ^ a[n][n] * x[n] = 0
這樣很明顯用高斯消元求矩陣的秩,進而求出自由變量的個數ans,那麼方程組X[i]的解的個數爲2^ans,注意要減去一個數都不選(即X[i]全爲0的情況),故答案爲2^ans-1。
代碼如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstdlib>
#include<sstream>
#include<deque>
#include<stack>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-6;
const int maxn = 2000 + 10;
const int maxt = 300 + 10;
const int mod = 10;
const int dx[] = {1, -1, 0, 0};
const int dy[] = {0, 0, -1, 1};
const int Dis[] = {-1, 1, -5, 5};
const int inf = 0x3f3f3f3f;
const int MOD = 1000000007;
ll n, m, k;
int prime[500];//素數表
bool vis[maxn];
int a[maxn][maxn];//a[i][j]表示數字j中含有所含質因子i的個數,奇數個爲1,偶數個爲0
ll num[maxn];
int cnt;
void getPrime(){//2000以內素數打表
memset(prime, 0, sizeof prime);
memset(vis, false, sizeof vis);
int maxnum = 2000;
cnt = 0;
for(int i = 2; i <= maxnum; ++i)if(!vis[i]){
prime[++cnt] = i;
vis[i] = true;
for(int j = i * i; j <= maxnum; j += i){
vis[j] = true;
}
}
}
ll Gauss(){//高斯消元
int i, j, k, x, y;
for(i = 1, j = 1; i <= cnt && j <= n; ++j){
k = i;
while(k <= cnt && !a[k][j]) ++k;
if(a[k][j]){
swap(a[i], a[k]);
for(x = i + 1; x <= cnt; ++x){
if(a[x][j]){
for(y = i; y <= n; ++y){
a[x][y] ^= a[i][y];
}
}
}
++i;
}
}
return (ll)n - (ll)i + 1ll;//自由變量的個數,每個自由變量可以爲0也可以爲1,故解的個數就是(1<<自由變量個數)
}
ll quick_pow(ll a, ll x){//快速冪
ll ans = 1;
while(x > 0){
if(x & 1) ans = (ans * a) % MOD;
x >>= 1;
a = (a * a) % MOD;
}
return ans;
}
int main(){
getPrime();
int t, kase = 0;
scanf("%d", &t);
while(t--){
scanf("%I64d", &n);
ll tmp;
memset(a, 0, sizeof a);
for(int i = 1; i <= n; ++i){
scanf("%lld", &num[i]);
tmp = num[i];
for(int j = 1; j <= cnt; ++j){//求每個數所含質因子及其個數
if(tmp % prime[j] == 0){
while(tmp > 0 && tmp % prime[j] == 0){
a[j][i] ^= 1;//異或判斷有奇數個還是偶數個
tmp /= prime[j];
}
}
}
}
ll x = Gauss();
printf("Case #%d:\n", ++kase);
printf("%lld\n", quick_pow(2, x) - 1);//減去全爲0(也就是一個數都不選)的情況
}
return 0;
}
/*
2
3
3 3 4
3
2 2 2
*/