[lnsyoj895C/luoguCF895C] Square Subsets
题意
给出一个序列 \(a\),求满足乘积是完全平方数的非空子集个数和。
sol
由于完全平方数一定需要满足质因数的个数均为偶数,所以可以设 \(state\) 表示某个数的质因数个数是奇数还是偶数,合并时用异或即可。
考虑一种稍暴力的写法:设 \(f_{i,state}\) 表示到 \(a_i\) 时,子集乘积状态为 \(state\) 的方案个数,设 \(s_i\) 表示数 \(a_i\) 对应的状态。
容易得出 \(f_{i,state}=f_{i-1,state\oplus s_i} + f_{i-1,state}\),其中前者为选择该数,后者为不选择该数。
状态数不可接受。
考虑到值域为 \(70\),因此有很多重复的数,而这些数的顺序并无关联,因此考虑变更状态为 \(f_{i,state}\) 表示到数 \(i\) 时,子集乘积状态为 \(state\) 的方案个数。
假设对于某个数,数列中有 \(k\) 个,那么选择偶数个这个数的方案数为
\[\sum_{i=0, i\equiv\pmod2}^k C_k^i = \sum_{i=0}^{\lfloor \frac{k}{2} \rfloor} C_k^{2i}=\sum_{i=0}^{\lfloor \frac{k}{2} \rfloor}C_{k-1}^{2i-1}+C_{k-1}^{2i}=\sum_{i=0}^{k-1}C_{k-1}^i=2^{k-1}
\]
奇数同理为 \(2^{k-1}\),因此 \(f_{i,state}=f_{i-1,state\oplus s_i} \cdot 2^{k-1} + f_{i-1,state} \cdot 2^{k-1}\),其中前者为选择奇数个,后者为选择偶数个。
代码
#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);
}

浙公网安备 33010602011771号