1. 程式人生 > 其它 >線性基模板

線性基模板

描述

線性基:給定\(n\)個非負整數,將每個整數的二進位制看作是向量,求這些向量的一組基。

應用

  • 求一個集合\(S\)中取一個子集異或得到所有數的數量
  • 求一個集合\(S\)中取一個子集異或可以得到的最大/小值
  • 求一個集合\(S\)中取一個子集異或可以得到的第\(k\)大/小值
  • 求一個集合\(S\)中取一個子集異或是否可以得到非負整數\(x\)

程式碼

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

using namespace std;

typedef long long ll;

const int N = 65;

int n;
ll b[N];
ll tmp[N];
bool flag;

void ins(ll x) //插入數字,獲取線性基
{
    for(int i = 62; i >= 0; i --) {
        if(x >> i & 1) {
            if(!b[i]) {
                b[i] = x;
                return;
            }
            else x ^= b[i];
        }
    }
    flag = true;
}

ll find_max() //找到能被表示出來的最大值
{
    ll ans = 0;
    for(int i = 62; i >= 0; i --) {
        ans = max(ans, ans ^ b[i]);
    }
    return ans;
}

ll find_min() //找到能被表示出來的最小值
{
    for(int i = 0; i <= 62; i ++) {
        if(b[i]) {
            return b[i];
        }
    }
}

ll get_kth(ll k) //找到第k小的能被表示出來的數
{
    ll res = 0;
    int cnt = 0;
    k -= flag;
    if(!k) return 0;
    for(int i = 0; i <= 62; i ++) {
        for(int j = i - 1; j >= 0; j --) {
            if(b[i] >> j & 1) b[i] ^= b[j];
        }
        if(b[i]) tmp[cnt++] = b[i];
    }
    if(k >= (1ll << cnt)) return -1;
    for(int i = 0; i < cnt; i ++) {
        if(k >> i & 1) {
            res ^= tmp[i];
        }
    }
    return res;
}

bool find_x(ll x) //x是否能被線性基表示出來
{
    for(int i = 62; i >= 0; i --) {
        if(x >> i & 1) {
            if(!b[i]) return false;
            x ^= b[i];
        }
    }
    return true;
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) {
        ll x;
        scanf("%lld", &x);
        ins(x);
    }
    ll ans = find_max();
    printf("%lld\n", ans);
    return 0;
}