线性基(解决子集异或和问题)

适用:解决集合异或和问题(第k小异或,最大异或,能否异或和组成给定的数)

用途:主要给个S集合,能通过动态插入每个数得到大小为log{max ai}的线性基B,而B中的所有情况异或和都唯一对应于S中的每个数

注意:线性基中没有异或和为0的子集,也就是说当线性基集合小于S时,证明在S中有些集合异或和为0;

模板题:https://vjudge.net/problem/SGU-275

题意:求能异或和最大的值

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M=60;
struct LB{
    ll a[M+2];
    void init(int n){
        for(int i=0;i<=M;i++) a[i]=0;
    }
    void Insert(ll t){
        // 逆序枚举二进制位
        for (int j = M; j >= 0; j--){
            // 如果 t 的第 j 位为 0,则跳过
            if (!(t & (1ll << j))) continue;

            // 如果 a[j] != 0,则用 a[j] 消去 t 的第 j 位上的 1
            if (a[j]) t ^= a[j];
            else{
                // 找到可以插入 a[j] 的位置

                // 用 a[0...j - 1] 消去 t 的第 [0, j) 位上的 1
                // 如果某一个 a[k] = 0 也无须担心,因为这时候第 k 位不存在于线性基中,不需要保证 t 的第 k 位为 0
                for (int k = 0; k < j; k++) if (t & (1ll << k)) t ^= a[k];

                // 用 t 消去 a[j + 1...L] 的第 j 位上的 1
                for (int k = j + 1; k <= M; k++) if (a[k] & (1ll << j)) a[k] ^= t;

                // 插入到 a[j] 的位置上
                a[j] = t;

                // 不要忘记,结束插入过程
                return;
            }

            // 此时 t 的第 j 位为 0,继续寻找其最高位上的 1
        }

        // 如果没有插入到任何一个位置上,则表明 t 可以由 a 中若干个元素的异或和表示出,即 t 在 span(a) 中
    }
    ll getmax(){
        ll ans=0;
        for(int i=M;i>=0;i--)
            ans^=a[i];
        return ans;
    }
}lb;
int main(){
    int n;
    scanf("%d",&n);
    lb.init(n);
    for(int i=1;i<=n;i++){
        ll x;
        scanf("%lld",&x);
        lb.Insert(x);
    }
    printf("%lld\n",lb.getmax());
    return 0;
}
View Code

学习粗:https://oi.men.ci/linear-basis-notes/

posted @ 2020-10-12 13:24  starve_to_death  阅读(349)  评论(0编辑  收藏  举报