Zhu and 772002 HDU - 5833 (高斯消元求异或方程组解的个数)

题目链接: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

*/


a[i][1] *x[1] ^ a[i][2] * x[2] ^ ...... ^ a[i][n] * x[n] = 0
a[i][1] *x[1] ^ a[i][2] * x[2] ^ ...... ^ a[i][n] * x[n] = 0
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章