跳转至

线性基

概念

线性基是指在数学中,尤其是在线性代数领域,一个由一组向量组成的集合,这些向量的点积为1,且任何其他向量都可以表示为这个集合中向量的线性组合。

简单来说,线性基就是一个向量空间的一组基本向量,这些向量在一起构成了整个空间的基底。

异或线性基

异或线性基: 一个数列 A,P 是一组异或线性基,则:

  1. P 中任意非空子集的异或和不为0

  2. P 中数的最高位1不重复

  3. P 中数的个数≤A中数的个数

  4. A中任意数字都能通过P中的一些数字异或出来

异或线性基的构造方法:贪心法和高斯消元法 因为高斯消元法构造的异或线性基有更好的性质,能更加方便地解决问题。

例题 #1【模板】线性基

给定 \(n\) 个整数(数字可能重复),求在这些数中选取任意个,使得他们的异或和最大。

\(1 \leq n \leq 50, 0 \leq S_i < 2 ^ {50}\)

实际上线性基的时间复杂度为O(63n),n可以是\(10^5\)


思路: 1.先构造出的n个整数的异或线性基 2.n个整数的子集的最大异或和等于其线性基的最大异或和 3.直接将线性基中所有元素都异或起来即是答案

这里的线性基表示:取这些线性基中的任意个元素的异或和,可以得到集合中的每一个元素。并且线性基之间不可互相表示(这是基的概念了)。

高斯消元法

/*
                      Keyblinds Guide
                    ###################
      @Ntsc 2024

      - Ctrl+Alt+G then P : Enter luogu problem details
      - Ctrl+Alt+B : Run all cases in CPH
      - ctrl+D : choose this and dump to the next
      - ctrl+Shift+L : choose all like this
      - ctrl+K then ctrl+W: close all
      - Alt+la/ra : move mouse to pre/nxt pos'

*/
#include <bits/stdc++.h>
#include <queue>
using namespace std;

#define rep(i, l, r) for (int i = l, END##i = r; i <= END##i; ++i)
#define per(i, r, l) for (int i = r, END##i = l; i >= END##i; --i)
#define pb push_back
#define mp make_pair
#define int long long
#define pii pair<int, int>
#define ps second
#define pf first
#define ull unsigned long long

#define itn int
// #define inr int
// #define mian main
// #define iont int

#define rd read()
int read(){
    int xx = 0, ff = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            ff = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
      xx = xx * 10 + (ch - '0'), ch = getchar();
    return xx * ff;
}
void write(int out) {
    if (out < 0)
        putchar('-'), out = -out;
    if (out > 9)
        write(out / 10);
    putchar(out % 10 + '0');
}

#define ell dbg('\n')
const char el='\n';
const bool enable_dbg = 1;
template <typename T,typename... Args>
void dbg(T s,Args... args) {
    if constexpr (enable_dbg){
    if(1)cerr<<' ';
    cerr << s;
        if constexpr (sizeof...(Args))
            dbg(args...);
    }
}

#define zerol = 1
#ifdef zerol
#define cdbg(x...) do { cerr << #x << " -> "; err(x); } while (0)
void err() { cerr << endl; }
template<template<typename...> class T, typename t, typename... A>
void err(T<t> a, A... x) { for (auto v: a) cerr << v << ' '; err(x...); }
template<typename T, typename... A>
void err(T a, A... x) { cerr << a << ' '; err(x...); }
#else
#define dbg(...)
#endif


const int N = 3e5 + 5;
const int INF = 1e18;
const int M = 1e7;
const int MOD = 1e9 + 7;

int cnt;
int n;
int a[N];

void gauss(){
    for(int i=62;~i;i--){
        for(int j=cnt;j<n;j++){
            if(a[j]>>i&1){
                swap(a[j],a[cnt]);
                break;
            }
        }

        if(!(a[cnt]>>i&1))continue;

        for(int j=0;j<n;j++){
            if(j!=cnt&&(a[j]>>i&1))a[j]^=a[cnt];
        }
        cnt++;
        if(cnt==n)break;

    }

}

void solve(){
    n=rd;
    for(int i=0;i<n;i++){
        a[i]=rd;
    }

    gauss();


    int ans=0;
    for(int i=0;i<cnt;i++){
        ans^=a[i];
    }

    cout<<ans<<endl;
}

signed main() {
    // freopen(".in","r",stdin);
    // freopen(".in","w",stdout);

    int T=1;
    while(T--){
        solve();
    }
    return 0;
}

贪心法

贪心法构造的线性基可能会有高位重复的情况(即\(p_i\)的最高位在\(p_j\)中也为1,但是仍然保证没有两个p的最高位相同)。因此在取异或和最大值时要从大到小枚举线性基并贪心地与ans异或。

/*
                      Keyblinds Guide
                    ###################
      @Ntsc 2024

      - Ctrl+Alt+G then P : Enter luogu problem details
      - Ctrl+Alt+B : Run all cases in CPH
      - ctrl+D : choose this and dump to the next
      - ctrl+Shift+L : choose all like this
      - ctrl+K then ctrl+W: close all
      - Alt+la/ra : move mouse to pre/nxt pos'

*/
#include <bits/stdc++.h>
#include <queue>
using namespace std;

#define rep(i, l, r) for (int i = l, END##i = r; i <= END##i; ++i)
#define per(i, r, l) for (int i = r, END##i = l; i >= END##i; --i)
#define pb push_back
#define mp make_pair
#define int long long
#define pii pair<int, int>
#define ps second
#define pf first
#define ull unsigned long long

#define itn int
// #define inr int
// #define mian main
// #define iont int

#define rd read()
int read(){
    int xx = 0, ff = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            ff = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
      xx = xx * 10 + (ch - '0'), ch = getchar();
    return xx * ff;
}
void write(int out) {
    if (out < 0)
        putchar('-'), out = -out;
    if (out > 9)
        write(out / 10);
    putchar(out % 10 + '0');
}

#define ell dbg('\n')
const char el='\n';
const bool enable_dbg = 1;
template <typename T,typename... Args>
void dbg(T s,Args... args) {
    if constexpr (enable_dbg){
    if(1)cerr<<' ';
    cerr << s;
        if constexpr (sizeof...(Args))
            dbg(args...);
    }
}

#define zerol = 1
#ifdef zerol
#define cdbg(x...) do { cerr << #x << " -> "; err(x); } while (0)
void err() { cerr << endl; }
template<template<typename...> class T, typename t, typename... A>
void err(T<t> a, A... x) { for (auto v: a) cerr << v << ' '; err(x...); }
template<typename T, typename... A>
void err(T a, A... x) { cerr << a << ' '; err(x...); }
#else
#define dbg(...)
#endif


const int N = 3e5 + 5;
const int INF = 1e18;
const int M = 1e7;
const int MOD = 1e9 + 7;

int cnt;
int n;
int p[N];

void insert(int x){
    for(int i=63;~i;i--){
        if(x>>i&1){
            if(p[i])x^=p[i];
            else {
                p[i]=x;
                break;
            }
        }
    }
}
void solve(){
    n=rd;
    for(int i=0;i<n;i++){
        insert(rd);
    }

    int ans=0;
    for(int i=63;~i;i--){
//      cdbg(p[i]);
        if((ans^p[i])>ans)ans^=p[i];//注意优先级
    }

    cout<<ans<<endl;
}

signed main() {
    // freopen(".in","r",stdin);
    // freopen(".in","w",stdout);

    int T=1;
    while(T--){
        solve();
    }
    return 0;
}

/*
4
6 5 9 10
*/