Petya又迟到了...老师给了他额外的任务。对于一些数组a,Petya需要找到从中间选择非空子集,使它们的乘积等于某个整数的平方的方法的数量。 如果这些方法所选择的元素的索引不同,则认为这两种是不同的方法。 因为结果可能很大,结果需要\(mod \; 10^9+7\)
考虑到完全平方数只和质因子次幂的奇偶性相关,那么我们可以把乘积转化为二进制下的异或。
于是题目变成选择若干个数异或起来为\(0\)的方案数。
那么考虑把所有元素插入到线性基中,于是不在线性基里的元素一定可以被线性基里的元素表示,相异或就可以为\(0\)。
而在线性基里的元素一定不会其他元素表示,相异或一定不为\(0\)。
所以方案数就是不在线性基中的元素个数组成的非空子集数:\(2^{n-cnt}-1\)
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 1e5;
const int P = 1e9 + 7;
using namespace std;
int n,a[N + 5],prime[N + 5],pcnt,vis[N + 5],val[N + 5],p[N + 5],cnt;
int mypow(int a,int x){int s = 1;for (;x;x & 1 ? s = 1ll * s * a % P : 0,a = 1ll * a * a % P,x >>= 1);return s;}
void prework()
{
for (int i = 2;i <= 70;i++)
{
if (!vis[i])
prime[++pcnt] = i;
for (int j = 1;prime[j] * i <= 70 && j <= pcnt;j++)
{
vis[i * prime[j]] = 1;
if (i % prime[j] == 0)
break;
}
}
}
void ins(int x)
{
for (int i = 22;i >= 0;i--)
if (x >> i & 1)
{
if (!p[i])
{
p[i] = x;
cnt++;
break;
}
x ^= p[i];
}
}
int main()
{
scanf("%d",&n);
for (int i = 1;i <= n;i++)
scanf("%d",&a[i]);
prework();
for (int i = 1;i <= n;i++)
{
for (int j = 1;j <= pcnt;j++)
while (a[i] % prime[j] == 0)
val[i] ^= 1 << j - 1,a[i] /= prime[j];
ins(val[i]);
}
int ans = mypow(2,n - cnt) - 1;
cout<<(ans + P) % P<<endl;
return 0;
}