题目链接 Square Subsets
这是白书原题啊
先考虑状压DP的做法
2到70总共19个质数,所以考虑状态压缩。
因为数据范围是70,那么我们统计出2到70的每个数的个数然后从2考虑到70。
设dp[x][mask]为考虑到x这个数的时候,x这个数和之前的所有数中,选出某些数,他们的乘积分解质因数,所有的指数对2取模之后,
状态为mask的方案数。
然后就可以转移了……这个状压DP花了我好几个小时……真是弱啊
哦对最后还要特判1的情况。
每个1选或不选都可以,然后考虑只选1的情况,累加即可。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const LL mod = 1e9 + 7; const int N = 3e6 + 10; const int M = 1e5 + 10; int c[101], p[30], m[101]; int n, x, cnt, now, all; bool flag; LL two[M], f[2][N]; void up(LL &a, LL b) { a = (a + b) % mod;} void init(){ two[0] = 1; rep(i, 1, 100000) two[i] = two[i - 1] * 2 % mod; scanf("%d", &n); rep(i, 1, n) scanf("%d", &x), ++c[x]; rep(i, 2, 70){ flag = true; rep(j, 2, i - 1) if (i % j == 0){ flag = false; break; } if (flag) p[cnt++] = i; } rep(i, 1, 70){ int y = i; rep(j, 0, cnt - 1){ int tt = 0; while (y % p[j] == 0) y /= p[j], ++tt; if (tt & 1) m[i] |= (1 << j); } } } int main(){ init(); all = (1 << cnt) - 1; rep(i, 2, 70){ if (c[i] == 0) continue; memset(f[now ^ 1], 0, sizeof f[now ^ 1]); LL a1 = two[c[i] - 1], a2 = (a1 - 1 + mod) % mod; up(f[now ^ 1][m[i]], a1); up(f[now ^ 1][0], a2); rep(mask, 0, all) up(f[now ^ 1][mask ^ m[i]], f[now][mask] * a1 % mod); rep(mask, 0, all) up(f[now ^ 1][mask], f[now][mask] * a2 % mod); rep(mask, 0, all) up(f[now ^ 1][mask], f[now][mask]); now ^= 1; } LL ans = f[now][0]; ans = (ans * two[c[1]]) % mod; up(ans, (two[c[1]] - 1 + mod) % mod); printf("%lld\n", ans); return 0; }
还有一种就是考虑异或线性基的做法。
如果一个数可以被当前线性基中的数表示出来,那么这个数就相当于一个完全平方数。
选与不选两种状态。
令最后线性基中的数的个数为$x$
最后答案就是$2^{n - x} - 1$
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const LL mod = 1e9 + 7; const int N = 3e6 + 10; const int M = 1e5 + 10; int c[101], p[30], m[101]; int n, x, cnt, now, all; bool flag; LL two[M], f[2][N]; void up(LL &a, LL b) { a = (a + b) % mod;} void init(){ two[0] = 1; rep(i, 1, 100000) two[i] = two[i - 1] * 2 % mod; scanf("%d", &n); rep(i, 1, n) scanf("%d", &x), ++c[x]; rep(i, 2, 70){ flag = true; rep(j, 2, i - 1) if (i % j == 0){ flag = false; break; } if (flag) p[cnt++] = i; } rep(i, 1, 70){ int y = i; rep(j, 0, cnt - 1){ int tt = 0; while (y % p[j] == 0) y /= p[j], ++tt; if (tt & 1) m[i] |= (1 << j); } } } int main(){ init(); all = (1 << cnt) - 1; rep(i, 2, 70){ if (c[i] == 0) continue; memset(f[now ^ 1], 0, sizeof f[now ^ 1]); LL a1 = two[c[i] - 1], a2 = (a1 - 1 + mod) % mod; up(f[now ^ 1][m[i]], a1); up(f[now ^ 1][0], a2); rep(mask, 0, all) up(f[now ^ 1][mask ^ m[i]], f[now][mask] * a1 % mod); rep(mask, 0, all) up(f[now ^ 1][mask], f[now][mask] * a2 % mod); rep(mask, 0, all) up(f[now ^ 1][mask], f[now][mask]); now ^= 1; } LL ans = f[now][0]; ans = (ans * two[c[1]]) % mod; up(ans, (two[c[1]] - 1 + mod) % mod); printf("%lld\n", ans); return 0; }
时间: 2024-10-08 18:38:04