[lnsyoj895C/luoguCF895C] Square Subsets

题意

给出一个序列 a,求满足乘积是完全平方数的非空子集个数和。

sol

由于完全平方数一定需要满足质因数的个数均为偶数,所以可以设 state 表示某个数的质因数个数是奇数还是偶数,合并时用异或即可。
考虑一种稍暴力的写法:设 fi,state 表示到 ai 时,子集乘积状态为 state 的方案个数,设 si 表示数 ai 对应的状态。
容易得出 fi,state=fi1,statesi+fi1,state,其中前者为选择该数,后者为不选择该数。
状态数不可接受。
考虑到值域为 70,因此有很多重复的数,而这些数的顺序并无关联,因此考虑变更状态为 fi,state 表示到数 i 时,子集乘积状态为 state 的方案个数。
假设对于某个数,数列中有 k 个,那么选择偶数个这个数的方案数为

i=0,i(mod2)kCki=i=0k2Ck2i=i=0k2Ck12i1+Ck12i=i=0k1Ck1i=2k1

奇数同理为 2k1,因此 fi,state=fi1,statesi2k1+fi1,state2k1,其中前者为选择奇数个,后者为选择偶数个。

代码

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;

const int N = 75, M = 25, K = 1 << 19, S = 100005, mod = 1e9 + 7;

int cnt[N], state[N];
int primes[M] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67};
int n;
int pow2[S];
int f[N][K];

int main(){
    scanf("%d", &n);
    pow2[0] = 1;
    for (int i = 1; i <= n; i ++ ) {
        int t;
        scanf("%d", &t);
        cnt[t] ++ ;
        pow2[i] = (LL) pow2[i - 1] * 2 % mod;
    }

    for (int i = 1, k = 1; i <= 70; i ++ , k = i) 
        for (int j = 0; j < 19; j ++ )
            if (k % primes[j] == 0)
                while (k % primes[j] == 0)
                    state[i] ^= 1 << j, k /= primes[j];
    
    f[0][0] = 1;
    int lst = 0;
    for (int i = 1; i <= 70; i ++ ) {
        if (!cnt[i]) continue;
        for (int s = 0; s < K; s ++ ) {
            int ns = s ^ state[i];
            f[i][s] = (LL) f[lst][s] * pow2[cnt[i] - 1] % mod;
            f[i][s] = (f[i][s] + (LL) f[lst][ns] * pow2[cnt[i] - 1] % mod) % mod;
        }
        lst = i;
    }

    printf("%d\n", (f[lst][0] - 1 + mod) % mod);
}
posted @   是一只小蒟蒻呀  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
点击右上角即可分享
微信分享提示