CF895C Square Subsets

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;
}
posted @ 2020-09-17 08:19  eee_hoho  阅读(164)  评论(0编辑  收藏  举报