zoukankan      html  css  js  c++  java
  • 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
    ", (pow(2, n - lb.sz, mod) - 1 + mod) % mod);
        return 0;
    }
    
    I‘m Stein, welcome to my blog
  • 相关阅读:
    爬虫入门
    读写文件操作
    列表的操作
    课后习题小练
    Python切片
    逗号的麻烦
    字符串学与练
    Turtle的学习
    FTL(FreeMarker)基础
    java反射机制基础
  • 原文地址:https://www.cnblogs.com/Emcikem/p/14402286.html
Copyright © 2011-2022 走看看