【2016 CCPC 网络赛 】B Zhu and 772002

Zhu and 772002

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2637    Accepted Submission(s): 944


 

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

Author

UESTC

Source

2016中国大学生程序设计竞赛 - 网络选拔赛

问题链接:HDU5833 Zhu and 772002

题目大意:给你n个数,选择其中的一些数(至少选择一个)使得它们的乘积是一个完全平方数,问总的方案模上1000000007的结果。

题解:看看完全平方数的特点:每个质因数的个数为偶数,比如4=2*2 ,质因数2有偶数个,36=2*2*3*3,质因数2和3都有偶数个,因此需要把每个数分解为质因数形式,每个数有取和不取两种情况,使用搜索进行组合判断是一个方案,但是暴力搜索一定会超时,这时就需要用一种新的方法解决:线性方程组,b[i][j]是这个线性方程组的系数矩阵,b[i][j]表示第j个数的第i个因子为偶数(用0表示)还是为奇数(用1表示),求解这个线性方程组的自由元的个数即可解决问题(自由元是指这个未知数取任何值都行,此问题就是取和不取两种值),因此结果是 2^(自由元个数)-1 ,使用快速模幂法求解此式子。自由元的个数如何求解,可以使用高斯消元法进行求解,具体看代码:

AC的C++程序:

#include<iostream>
#include<cstring>

using namespace std;

typedef long long ll;

//快速模幂法
ll f(ll x,ll n)
{
	ll res=1;
	while(n>0)
	{
		if(n&1)
		  res=res*x%1000000007;
		x=x*x%1000000007;
		n>>=1;
	}
	return res;
}

int p[310];//不超过2000的素数只有304个 
int b[310][310];//a[i][j]表示第j个数的第i个因子的系数 
int len=0;

void init() //用p数组存储连续素数
{
	bool a[2005]={false};
	a[0]=a[1]=false;
	for(int i=2;i<=2000;i++)
	  if(!a[i])
	  {
	  	p[len++]=i;
	  	for(int j=i*i;j<=2000;j+=i)
	      a[j]=true;
	  }
}


ll Guass(int m,int n)//m个等式,n个变量 
{
	int i=0,j=0;//i标记行,j标记列
	while(i<m&&j<n)
	{
		int r;
		//找到j列第一个不为0的行
		for(r=i;r<m&&b[r][j]==0;r++);
		if(b[r][j])//如果在第j列找到了不为0的行 
		{
			if(r!=i)//r不等于i,就把第r行和第i行进行交换
			 for(int k=0;k<n;k++)
			   swap(b[i][k],b[r][k]);
			//把i行以下的行进行变零操作
			for(int k=i+1;k<m;k++)
			  if(b[k][j])//如果为零就不用进行操作,非零就要进行异或操作 
			    for(int t=0;t<n;t++)
				  b[k][t]^=b[i][t];//^为异或运算符 
			i++;//此行操作完,接着操作下一行 
		}
	    j++; 
	}
	return f(2,n-i)-1;//结果 
}

int main()
{
	init();
	int t,n;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		memset(b,0,sizeof(b));
		scanf("%d",&n);
		for(int j=0;j<n;j++)
		{
			ll x;
			scanf("%lld",&x);
			//进行分解,分解为质因数乘积形式
			for(int k=0;k<len;k++)
			  while(x%p[k]==0)
			  {
			  	b[k][j]^=1;
			  	x/=p[k];
			  }
		}
		printf("Case #%d:\n%lld\n",i,Guass(len,n));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/SongBai1997/article/details/81778817
今日推荐