线性基总结(其实就是我的模板)

想知道线性基直接看大佬的https://blog.sengxian.com/algorithms/linear-basis, 我就是弄下我常用的操作。

定义

学过线代里面的“基向量”,基向量说的就是一组可以表示整个空间的向量。比如在三维空间中一个基向量就是(1,0,0) (0,1,0) (0,0,1)。线性基的定义也类似,给你一个数组a[ ],它的线性基就是一组可以表示数组a中任意一个数的一个数组p[ ]。这个表示的意思就和前面的基向量表示的意思类似,是通过p中几个元素的异或得到a中的任意一个元素。

性质

1 原序列里面的任意一个数都可以由线性基里面的一些数异或得到
2 线性基里面的任意一些数异或起来都不能得到0
3 线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的

#include<bits/stdc++.h>
#define mp make_pair
#define met(a, b) memset(a, b, sizeof(a))
#define ll long long
using namespace std;
ll p[70], newp[70], cnt;

void getp(ll x){ //构建
    for(ll i = 63; i >= 0; i--){
        if(!(x>>i)) continue;
        if(!p[i]){
            p[i] = x;
            break;
        }
        x ^= p[i];
    }
}

ll getmax(){ //求异或最大值
    ll ans = 0;
    for(int i = 63; i >= 0; i--){
        if((ans ^ p[i]) > ans) ans ^= p[i];
    }
    return ans;
}

ll getmin(){ //求异或最小值
    for(int i = 0; i <= 63; i++){
        if(p[i]) return p[i];
    }
    return 0;
}

bool judge(ll x){ //判断x是不是数组a中的
    for(ll i = 63; i >= 0; i--){
        if(x>>i) x ^= p[i];
        if(!x) return 1;
    }
    return 0;
}

void rebuild(){ //求第k小的准备工作
    cnt = 0;
    for(ll i = 63; i >= 0; i --){
        for(ll j = i - 1; j >= 0; j --)
            if(p[i] & (1 << j)) p[i] ^= p[j];
    }
    for(ll i = 0; i <= 63; i ++){
        if(p[i]) newp[cnt++] = p[i];
    }
}

ll getans(ll k){ //求第k小
    if(k >= (1ll << cnt)) return -1;
    ll ret = 0;
    for(ll i = 63; i >= 0; i --){
        if(k & (1ll << i)) ret ^= newp[i];
    }
    return ret;
}


int main(){
    int n;  cin >> n;
    for(int i = 1; i <= n; i++){
        ll x; cin >> x;
        getp(x);
    }
    rebuild();
    int m; cin >> m;
    while(m--){
        ll k; cin >> k;
        if(n != cnt) k--;  //注意这个
        cout << getans(k) << endl;
    }
    return 0;
}

 

posted @ 2019-08-27 20:31  philo_zhou  阅读(218)  评论(0编辑  收藏  举报