Codeforces Round #448 (Div. 2): C. Square Subsets(线性基)

time limit per test
4 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Petya was late for the lesson too. The teacher gave him an additional task. For some array a Petya should find the number of different ways to select non-empty subset of elements from it in such a way that their product is equal to a square of some integer.

Two ways are considered different if sets of indexes of elements chosen by these ways are different.

Since the answer can be very large, you should find the answer modulo 109 + 7.

Input

First line contains one integer n (1 ≤ n ≤ 105) — the number of elements in the array.

Second line contains n integers ai (1 ≤ ai ≤ 70) — the elements of the array.

Output

Print one integer — the number of different ways to choose some elements so that their product is a square of a certain integer modulo 109 + 7.

Examples
input
4
1 1 1 1
output
15
input
4
2 2 2 2
output
7
input
5
1 2 4 5 8
output
7


会线性基的话,这题就是5分钟题

不然要想很久+码很久的状压DP,或者卡到比赛结束


题意:

给你n个数,每个数<=70,问有多少个集合,满足集合中所有数相乘是个完全平方数(空集除外)


思路:

很容易想到的是:一个完全平方数分解质因数后所有的因子一定都出现偶数次,而这题的ai<=70

小于70的质数有且只有19个,这样就可以将每个数转成一个长度为19的二进制

第i位为1就说明分解质因数后第i个质数出现了奇数次,为0即出现偶数次,之后再把它转回十进制的另一个数

这样问题就变成了n个数,有多少个集合满足它们异或为0!


求出这n个数的线性基,答案就是2^(n-线性基内数的个数)-1

可以用高斯消元证明

#include<stdio.h>
#define mod 1000000007
#define LL long long
int a[100005], p[21];
int z[21] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67};
LL Pow(LL x, int y)
{
	LL ans = 1;
	while(y)
	{
		if(y%2)
			ans = ans*x%mod;
		x = x*x%mod;
		y /= 2;
	}
	return ans;
}
int main(void)
{
	int n, i, x, j, now, sum;
	scanf("%d", &n);
	for(i=1;i<=n;i++)
	{
		scanf("%d", &x);
		for(j=0;j<=18;j++)
		{
			now = 0;
			while(x%z[j]==0)
				x /= z[j], now ^= 1;
			a[i] |= now*(1<<j);
		}
	}
	for(i=1;i<=n;i++)
	{
		for(j=18;j>=0;j--)
		{
			if(a[i]&(1<<j))
			{
				if(p[j]==0)
				{
					p[j] = a[i];
					break;
				}
				else
					a[i] ^= p[j];
			}
		}
	}
	sum = n;
	for(j=0;j<=18;j++)
	{
		if(p[j])
			sum--;
	}
	printf("%lld\n", Pow(2ll, sum)-1);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值