线性基小结

前言

线性基里集合的线性组合能表示出原数组的异或组合

构造

依次插入数,从位数的高到低插入
如果该位无元素((0)),将目前元素放进去
如果改为有元素,异或后处理更低一位

解释构造与性质

插入失败:(xigotimes d[a]igotimes d[b]igotimes d[c]...igotimes d[z]=0)
插入成功:插入(y=xigotimes d[a]igotimes d[b]igotimes d[c]...igotimes d[z] e 0),利用(y)元素,实现异或全排列
所以线性基里的元素个数是最少的

在目前线性基中插入(b)(c)均失败的情况下
变化线性基(拿出原数组中的一个元素)若插入其中一个成功则另一个不成功
假设要插入(b)(c)(b=d_i igotimes d_j igotimes d_k...igotimes d_z)
把其中一个数删掉后,其实等同于自己替代了那个位置,(c)还是插不进

基础应用

异或最大值:贪心从高到低贪心选择或不选择
异或最小值:贪心从低到高位选择第一个非空元素
异或判特值:贪心处理直到为(0)
异或个数:因为每个元素都是处理出来相对独立的,答案为(2^{集合个数})
异或(K)小值:(2^i)小值就是第(i-1)个元素贪心地与前面处理的结果,然后对(K)拆位处理
线性基合并:暴力插入即可

# include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL bit[64], a[64], x, k;
int main(){
    int n, m, cnt=0;
    scanf("%d",&n);
    for(int i=0; i<n; ++i){
        scanf("%lld",&x);
        for(int j=50; ~j; --j){
            if(x&(1LL<<j)){
                if(!bit[j]) {bit[j] = x; break;}
                x ^= bit[j];
            }
        }
    }//普通构造 
    for(int i=50; ~i; --i)
        for(int j=i-1; ~j; --j)
            if(bit[i]&(1LL<<j)) 
			    bit[i] ^= bit[j];//
    for(int i=0; i<=50; ++i)
        if(bit[i])  
	        a[cnt++] = bit[i];//
    scanf("%d",&m);
    while(m--){
        scanf("%lld",&k);
        if(cnt != n) --k;
        if(k >= (1LL<<cnt)) puts("-1");
        else{
            LL ans = 0;
            for(int i=0; i<cnt; ++i)
                if(k&(1LL<<i)) ans ^= a[i];
            printf("%lld
",ans);
        }
    }
    return 0;
}

应用

区间查询异或最值
解法一:离线处理排序右端点,每次插入的时候越末插入越优,所以尽量把向量换成新插入的点 code
解法二:在线处理,每次继承(复制)前面一个线性基,由于元素小不影响复杂度,如上插入 code

每个物品都有价值与标号,求在选出的集合子集标号异或不为(0)的最大价值
由性质可得一个贪心做法:不断插入到线性基,插入成功则答案加上价格

无向边权图(1)(n)可重边路径异或最大值
预处理每个环的异或值,这个值是可以单独算的,而环与环之间的链其实通过两次异或能忽略不计
(1)(n)的链随便选就行了,如果有多条路径,这种环也可以通过异或得到最大链
然后通过线性基得到最大值

原文地址:https://www.cnblogs.com/y2823774827y/p/10540135.html