cf 895C Square Subsets - 线性基
给出一个序列,找到一个子集,使得子集内的元素乘积是平方数。
考虑平方数的特点,就是唯一分解定理之后,质因子的幂一定是偶数。
数字很小,那么质因子肯定是在\([1,70]\)以内的,有\(18\)个。
那么就把每个数字用二进制表示,可以用18位的二进制表示,0表示偶数次,1表示奇数次
那相当于是集合里面的元素任何位的1的个数都是偶数才行。因为两个数字相乘,就是每一个质因子表示的位的异或。
那么我就需要找到一些集合,使得异或和是0就行了。
那么考虑到线性基的特点,线性基里的数是最少集合使得线性基里的数字异或和是非0。
假设线性基里的元素个数是\(s\),那么答案就是非线性基里真子集的个数,就是\(2^{n-s}-1\)
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MaxBasis = 20;///二进制位数
const int mod = 1e9 + 7;
template<typename T = long long> inline T read() {
T s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) {s = (s << 3) + (s << 1) + ch - 48; ch = getchar();}
return s * f;
}
struct Linear_Basis {
ll base[MaxBasis + 10]; bool rel; int sz;
int tim[MaxBasis];
vector<ll> Basis;/// 线性基(向量)
Linear_Basis() { memset(base, 0, sizeof(base)); rel = false; sz = 0; Basis.clear();}
void init() {
memset(base, 0, sizeof(base)); rel = false; sz = 0; Basis.clear();
memset(tim, 0, sizeof(tim));
}
bool add(ll x) { //加入线性基中
for (int i = MaxBasis; i >= 0; --i) {
if (!(x >> i & 1)) continue;
if (base[i]) {x ^= base[i];}
else {
base[i] = x, ++sz;
return 1;
}
}
rel = true;
return 0;
}
ll Max(ll ans = 0) { //取最大
for(int i = MaxBasis; i >= 0; i--)
if(!(ans >> i & 1)) ans ^= base[i];
return ans;
}
ll Min(ll ans = 0) { //取最小
for(int i = 0; i <= MaxBasis; i++) ans = min(ans, ans ^ base[i]);
return ans;
}
void GetBasis() { //构造向量,用于之后求第k小
for (int i = 0; i <= MaxBasis; ++i)
if (base[i]) Basis.push_back(base[i]);
}
void bin(struct Linear_Basis &b) { // 线性基求并(合并)
for(int i = 0;i <= MaxBasis; i++) if(b.base[i]) add(b.base[i]);
}
Linear_Basis jiao(Linear_Basis a,Linear_Basis b){ // 线性基求交
Linear_Basis g, tmp = a;
ll cur, d;
for(int i = 0;i <= MaxBasis;i++) if(b.base[i]){
cur = 0,d = b.base[i];
for(int j = i;j >= 0;j--) if(d>>j&1){
if(tmp.base[j]){
d ^= tmp.base[j],cur ^= a.base[j];
if(d) continue;
g.base[i] = cur;
}else tmp.base[j] = d, a.base[j] = cur;
break;
}
}
return g;
}
ll Min_Kth(ll k) { // 线性基中第k小
if(rel) k--; // 线性基未满存在0
if(k >= (1ll << sz)) return -1;
ll ans = 0;
for(int i = 0; i < sz; i++) if(k & (1ll << i)) ans ^= Basis[i];
return ans;
}
} lb;
const int N = 5e5 + 5;
ll a[N];
ll pow(ll a, ll b, ll p){
ll ans = 1; a %= p;
while(b){
if(b & 1) ans = ans * a % p;
a = a * a % p;
b >>= 1;
}
return ans;
}
int pri[N], id[N], tot;
bool check(int n) {
if(n == 1) return 0;
for(int i = 2; i * i <= n; i++) if(n % i == 0) return 0;
return 1;
}
int main(){
int n = read();
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i <= 70; i++) if(check(i)) pri[++tot] = i, id[i] = tot;
for(int i = 1; i <= n; i++) {
int x = a[i]; a[i] = 0;
for(int j = 2; j * j <= x; j++) {
if(x % j == 0) {
int num = 0;
while(x % j == 0) x /= j, num++;
if(num & 1) a[i] |= 1 << id[j];
}
}
if(x > 1) a[i] |= 1 << id[x];
// cout << a[i] << endl;
}
for(int i = 1; i <= n; i++) lb.add(a[i]);
printf("%lld\n", (pow(2, n - lb.sz, mod) - 1 + mod) % mod);
return 0;
}
I‘m Stein, welcome to my blog