题解 UVA11542 【Square】

题意简介

\qquad 意思就是有 T T T组数据,每组数据中有 n n n个数,分别为 a 1 , a 2 , a 3 . . . a n a_{1},a_{2},a_{3}...a_{n} a1,a2,a3...an。从这n个数中任选一个或多个数使得所取出来的数的乘积为完全平方数,问有多少种取法。

\qquad 其中 1 ≤ T ≤ 30 , 1 ≤ N ≤ 100 , 1 ≤ a i ≤ 1 0 15 1 \leq T\leq 30,1 \leq N\leq 100,1 \leq a_{i}\leq 10^{15} 1T30,1N100,1ai1015并且每个数不包含大于 500 500 500的质因子

思路

\qquad 既然题目告诉我们每个数不含有大于 500 500 500的质因子,那么便从这一点下手。
\qquad 我们不妨以 { 4 , 6 , 10 , 15 } \{4,6,10,15\} {4,6,10,15}为例。首先我们知道完全平方数里的质因子都是成对出现的,先将四个数分解质因数。
\qquad 4 = 2 2 6 = 2 ∗ 3 10 = 2 ∗ 5 15 = 3 ∗ 5 4=2^2\quad6=2* 3\quad 10=2* 5\quad 15=3* 5 4=226=2310=2515=35
\qquad 从中我们可以知道这四个数中最大的质数是第三个质数
\qquad 那么我们可以由此列一个异或方程组:

表示2出现次数: ( 0 x o r X 1 ) x o r ( 1 x o r X 2 ) x o r ( 1 x o r X 3 ) x o r ( 0 x o r X 4 ) ≡ 0 ( m o d 2 ) (0xor X_{1})xor(1xorX_{2})xor(1xorX_{3})xor(0xorX_{4})\equiv0(mod\quad2) (0xorX1)xor(1xorX2)xor(1xorX3)xor(0xorX4)0(mod2)

表示3出现次数: ( 0 x o r X 1 ) x o r ( 1 x o r X 2 ) x o r ( 0 x o r X 3 ) x o r ( 1 x o r X 4 ) ≡ 0 ( m o d 2 ) (0xor X_{1})xor(1xorX_{2})xor(0xorX_{3})xor(1xorX_{4})\equiv0(mod\quad2) (0xorX1)xor(1xorX2)xor(0xorX3)xor(1xorX4)0(mod2)

表示5出现次数: ( 0 x o r X 1 ) x o r ( 0 x o r X 2 ) x o r ( 1 x o r X 3 ) x o r ( 1 x o r X 4 ) ≡ 0 ( m o d 2 ) (0xor X_{1})xor(0xorX_{2})xor(1xorX_{3})xor(1xorX_{4})\equiv0(mod\quad2) (0xorX1)xor(0xorX2)xor(1xorX3)xor(1xorX4)0(mod2)

\qquad 其中 X 1 , X 2 . . . X_{1},X_{2}... X1,X2...表示这个数取或不取,取则为 1 1 1,不取则为 0 0 0

\qquad 通过高斯消元我们就能找出这个方程中有 s s s个自由元,答案就是 2 s − 1 2^{s}-1 2s1

\qquad 由于题目要求ull自然溢出,所以设置ans的变量时注意一下就好了。

不了解异或方程组的童鞋请戳这里:http://www.cppblog.com/MatoNo1/archive/2012/05/20/175404.html

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int p=1e9+7;
bitset<505>f[505];//加快速率用bitset
ll n;
ll prime[505],h[505],num,s;
void pre() {
	for(int i=2; i<=500; i++) {
		if(!h[i])prime[++num]=i;
		for(int j=1; prime[j]*i<501; j++) {
			h[ prime[j]*i ]=1;
			if(!(i%prime[j]))break;
		}
	}
}
void solve() {
	scanf("%lld",&n);
	memset(f,0,sizeof(f));
	ll maxn=-1e9;//记录最大的素数是第几个
	ll x;
	for(int i=1; i<=n; i++) {
		scanf("%lld",&x);
		for(int j=1; prime[j]<=x&&j<=num; j++) {
			int k=prime[j];
			if(!(x%k)) {
				if(j>maxn)maxn=j;
				int w=0;
				while(!(x%k)) {
					w^=1;
					x/=k;
				}//确定初始值.
				f[j][i]=w;
			}
		}
	}
	ll pos=1;
	s=0;
	for(int i=1; i<=n; i++) {
		int w=0;
		for(int j=pos; j<=maxn; j++) {
			if(f[j][i]) {
				if(pos!=j)swap(f[j],f[pos]);
				for(int k=1; k<=maxn; k++) {
					if(k==pos)continue;
					if(f[k][i])f[k]^=f[pos];
				}
				w=1;
				pos++;
				break;
			}

		}
		if(!w)s++;//找有多少个自由元
	}
	unsigned long long  ans=1;//自然溢出
	for(int i=1; i<=s; i++)ans*=2;//由于s不大所以不用快速幂
	printf("%lld\n",ans-1);//最后答案要-1(排除全不选的情况)
}
int main() {
	pre();//欧拉筛筛素数.
	ll t;
	scanf("%lld",&t);
	while(t--)solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值