HDU 5833 高斯消元

Zhu and 772002

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)


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(1n300) ,next line there are n numbers a1,a2,...,an,(1ai1018) .
 

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
题意:给你n个数,现在可以取任意个数,他们的乘积是个完全平方数,一共有多少种取法。
题解:高斯消元法,先分解质因子,因为可以开方,所以对于一个答案,它的所有质因子都是偶数个,所以可以异或来考虑。
假设有x个自由变量,那么答案就是(2^x-1)%1000000007,因为题中说最少取一个数,所以要-1。
可以这样考虑,横列的自由变量的个数,就相当于纵列的n-r,r为旋转后矩阵的秩。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
ll m,prime[305],vis[2005];
ll a[305][305];
void init(){
    ll i,j;
    for(i=2;i<=2000;i++){//筛选素数
        if(!vis[i])prime[++m]=i;
        for(j=2;j*i<=2000;j++){
            vis[i*j]=1;
            if(!(i%j))break;
        }
    }
}
ll ranks(ll a[][305],ll m,ll n){
    ll i=1,j=1,k,r,u;
    while(i<=m&&j<=n){//处理第i个方程,第j个变量
        r=i;
        for(k=i;k<=m;k++){//寻找开头为1的第一个方程
            if(a[k][j]){
                r=k;
                break;
            }
        }
        if(a[r][j]){
            if(r!=i){//如果不是在开头,那就交换
                for(k=1;k<=n;k++)swap(a[r][k],a[i][k]);
            }
            for(u=i+1;u<=m;u++){//把下边的都消掉
                if(a[u][j]){
                    for(k=i;k<=n;k++){
                        a[u][k]^=a[i][k];
                    }
                }
            }
            i++;//矩阵的秩+1
        }
        j++;
    }
    return i-1;//因为开始的时候i为1,所以这里要-1
}
ll solve(ll n,ll x){//快速幂
	ll ans=1;
	while(x){
		if(x&1){
			ans=ans*n%1000000007;
		}
		n=n*n%1000000007;
		x>>=1;
	}
	return ans;
}
int main(){
    ll t,cas=1;
    init();
    scanf("%lld",&t);
    while(t--){
        memset(a,0,sizeof(a));
        ll maxp=0,x,n,i,j;
        scanf("%lld",&n);
        for(i=1;i<=n;i++){
            scanf("%lld",&x);
            for(j=1;j<=m;j++){
                while(x%prime[j]==0){//分解质因子
                    maxp=max(maxp,j);
                    x/=prime[j];
                    a[j][i]^=1;//矩阵翻转过来处理
                }
            }
        }
        ll r=ranks(a,maxp,n);
        printf("Case #%d:\n",cas++);
        cout<<((solve(2,n-r)-1+1000000007)%1000000007)<<endl;
    }
    return 0;
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值