1. 程式人生 > >hdu 5833 Zhu and 772002 (按位異或的高斯消元)

hdu 5833 Zhu and 772002 (按位異或的高斯消元)

最近都在打校積分賽 肝揹包

今天突然遇到了高斯消元 於是就 來一發吧

大白書160的題跟這個完全一模一樣啊

Problem Description

Zhu and 772002 are both good at math. One day, Zhu wants to test the ability of 772002, so he asks 772002 to solve a math problem. 

But 772002 has a appointment with his girl friend. So 772002 gives this problem to you.

There are n numbers a1,a2,...,an. The value of the prime factors of each number does not exceed 2000, you can choose at least one number and multiply them, then you can get a number b.

How many different ways of choices can make b is a perfect square number. The answer maybe too large, so you should output the answer modulo by 1000000007.

Input

First line is a positive integer T , represents there are T test cases.

For each test case:

First line includes a number n(1≤n≤300),next line there are n numbers a1,a2,...,an,(1≤ai≤1018).

Output

For the i-th test case , first output Case #i: in a single line.

Then output the answer of i-th test case modulo by 1000000007.

Sample Input

2 3 3 3 4 3 2 2 2

Sample Output

Case #1: 3 Case #2: 3

題目大意 : 給你幾個數 讓你從裡面隨便挑幾個 乘起來是完全平方數 一個數只能挑一次 至少要挑一個數

題目分析: 首先能想到的是通過質因分解 找幾個數質因數個數加起來為偶數的就行 比如 6=2*3  14=2*7  21=3*7 三個數乘起來的話 就=2*2 * 3*3 * 7*7 質因數都是偶數個數 就是完全平方數

難點在於我們怎麼知道挑哪幾個加起來質因數個數都是偶數呢

這裡我們需要轉變一下思維 我們列舉所有情況鐵定要超時  那麼我們可以試一下找有總共多少種可能的組合就行了 

我們可以對每個質因數 列一條方程  給定 6 7 14  對於質因數2我們有 1*x1+1*x2+0*x3=0 mod(2)

對於mod 2 我們乾脆就把方程轉化成異或 x1^x2=0  (這裡建議看看大白書P161,很詳細,我講得可能比較模糊)

對於所有質因數我們都列一條這樣的方程 組合成線性方程組 

方程組解的個數=2的自由變元個數次方

自由變元個數=自變數個數 (方程組幾列)- 約束變元個數 (階梯方程組有幾條方程首項不為0;首項不為0的方程的首項未知數就是約束變元)

通過求自由變元個數求方程組的解的個數

// 格式是按devc++碼的 建議複製到devc++看 可能註釋增廣矩陣那裡會序列
#include<bits/stdc++.h>
using namespace std;
long long a[2005][2005];
int prime[2000];
bool isprime[2005];
int pn;
int quick(long long int a,long long int b,long long int c) //快速冪模板 a的b次方取模c
{
    long long int ans=1;
    a=a%c;
    while(b!=0)
    {
        if(b&1) ans=(ans*a)%c;
        b>>=1;
        a=(a*a)%c;
    }
    return ans;  }
void getprime(int n){          // 素數篩選  prime存素數 isprime判斷素數
    int i,j;
	memset(isprime,true,sizeof(isprime));
	isprime[0]=isprime[1]=false;
	pn=0;
	for(i=2;i<n;i++){
	if(isprime[i]){
		prime[pn++]=i;
	}
	for(j=0;j<pn&&prime[j]*i<n;++j){
		isprime[prime[j]*i]=false;
		if(i%prime[j]==0)
		break;
	}
} }
int rankk(long long b[2005][2005],int m,int n){   // 按位異或高斯消元程式碼塊
   int i=0,j=0,k,r,u;
    while(i<m&&j<n){
        r=i;
        for(k=i;k<m;k++)
            if(b[k][j]) {               // 找一個方程第 j+1 個係數是 1 的 
                r=k;
                break;
            }
            if(b[r][j]){              // 第 r+1 個方程 
                if(r!=i) for(k=0;k<=n;k++) swap(b[r][k],b[i][k]);       // 如果 r 是 i  , 代表 第 i+1 條方程的j+1係數已經是最大的(1)了 , 不用換位置 
                for(u=i+1;u<m;u++) if(b[u][j])                          // 從 u 方程開始 (i方程的下一條)    如果 u方程的 j 位置不是0 
                    for(k=i;k<=n;k++) b[u][k]^=b[i][k];                 // 對 u 方程每一個位置 都按位異或 b[i][k]   比如 1 0 1   轉化成 1 0 1 
                i++;                                                    //                                               1 0 1          0 1 0  
            }
            j++;                                                        // 係數 ++        
        }
        return i;                                                       // i是約束變元的個數  
}
int main(){
   long long int t,i,j,k,ans;
   ios::sync_with_stdio(false);
   getprime(2005);
   while(cin>>t){
        k=1;
    while(t--){
            long long n,x;
        cin>>n;
        long long  maxp=0;
        memset(a,0,sizeof(a));
        for(i=0;i<n;i++){
            cin>>x;
            for(j=0;j<pn;j++)
            while(x%prime[j]==0){
                maxp=max(maxp,j);
                x/=prime[j];
                a[j][i]^=1;            // prime[j]是第 j+1 個方程 ;i 是第 i+1 個係數 
            }
        }
        long long int r=rankk(a,maxp+1,n);
        ans=quick(2,n-r,1000000007);          // n-r 是自由變元個數; 方程組解的個數等於 2 的自由變元個數次方 
    cout<<"Case #"<<k++<<":"<<endl;
 cout<<ans-1<<endl;                           // 減去方程未知數 x 全部為 0的情況; 即一個都不選 (把 0當作完全平方數的情況去掉 ; 題目不允許 0) 
        }
        }
        return 0;
    }

最後附上程式碼註釋