1. 程式人生 > >2016ccpc 1002(hdu5833)題解 (高斯消元求異或方程組自由變元)

2016ccpc 1002(hdu5833)題解 (高斯消元求異或方程組自由變元)

比賽結束才知道是個高斯消元的題目,嚇得我趕緊學了一發,然後驚訝的發現白皮書上原題QAQ.

由於剛學會,雖然是手敲但有些細節還是比對了模板,所以並不能解釋,先放一發程式碼,等熟練了再補.

-------------------------------------分割線---------------------------------------------

時隔兩個月終於有時間和精力來把這題來理一理了。

這個題比較重要的一點是想到把每個數分解成素數的冪次,這樣的話, 每個數相乘的結果,只要每個素數的冪都是偶數,就能保證乘積是平方數了。

所以把每個數分解,列出每個數在所有可能的素因子的冪的加和,要求結果為偶數,這樣的話等價於每一項都對2取模,每個等式右邊要求結果都為0。

如果能分解成n個素因子,那麼就有n個等式,每個等式等於號左邊是每個數在這個素因子上的冪對2取模,等式右邊為0.

現在求有幾種組合能使乘積為平方數,即線性方程組有多少個解,那麼簡單了,只需要求出自由變元數量ans,2的ans次-1即答案,因為題目要求不能一個數都不取,所以需要減去都為0的情況。求自用變元就是高斯消元模板了,在程式碼裡介紹吧。

程式碼:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
using namespace std;
int prim[400];
int v[2005];
long long x[400];
int a[400][400];
int k;
const int mod=1000000007;
void pri(int maxn)
{
    int i, j;
    for(i=2; i<=maxn; i++)
    {
        if(!v[i])
        {
            prim[k++]=i;
            for(j=i; j<=maxn; j+=i)
            {
               v[j]=0;                                                                                   v[j]=1;
            }
        }
    }
}
long long quick_mod(int m, long long n)
{
    long long ret=1;
    long long term=m;
    while(n>0)
    {
        if(n%2==1)ret=(ret*term)%mod;
        n>>=1;
        term=(term*term)%mod;
    }
    return ret;
}
int gauss(int var, int equ)
{
    int i, j;
    int col, maxr;
    int k=0;
    for(k=0, col=0; k<equ && col<var; k++, col++)
    {
        maxr=k;
        for(i=k; i<equ; i++)
        {
            if(abs(a[i][col])>abs(a[maxr][col]))maxr=i; //找到這一列最大的那個數,用於消去這一列以下的數,這裡為1即可
        }
        if(a[maxr][col]==0)  //如果這一列已經都為0,繼續對當前的行操作
        {
            k--;
            continue;
        }
        for(j=col; j<var+1; j++)
        {
            swap(a[k][j], a[maxr][j]);   //將選出的這一列最大的數對應的行交換到k行
        }
        for(i=k+1; i<equ; i++)
        {
           if(a[i][col]!=0)  for(j=col; j<var+1; j++)
            {
                {
                    a[i][j]^=a[k][j];  // 消去col的1
                }
            }
        }
    }
    for(i=k; i<equ; i++)
    {
        if(a[i][col]!=0)return -1;
    }
    if(var>k)return var-k;  //變元數減去秩即自由變元數
    return 0;
}
int main()
{
    int t;
    pri(2000);

    scanf("%d", &t);
    int e=1;
    while(t--)
    {
        int n;
        scanf("%d", &n);
        int i, j;
        memset(a,0,sizeof(a));
        for(i=0; i<n; i++)
        {
            scanf("%lld", &x[i]);
        }
        for(i=0; i<n; i++)
        {
            for(j=0; j<k; j++)
            {
                int c=0;
                while(x[i]%prim[j]==0)
                {
                    x[i]/=prim[j];
                    c++;
                }
                if(c&1)
                a[j][i]=1;
            }
        }
        long long ans=gauss(n, k);
        printf("Case #%d:\n", e++);
        printf("%lld\n", quick_mod(2,ans)-1);
    }
    return 0;
}