线性基入门

线性基是一种处理异或问题的数据结构,尤其适用于解决集合中的异或问题。本文介绍了线性基的概念、性质和操作,包括插入、验证、查询异或最小值和最大值,以及如何查询第k小的元素。同时,通过分析两道竞赛题目,展示了线性基在实际问题中的应用,如线性基结合线段树解决集合表示问题和维护区间异或最大值。
摘要由CSDN通过智能技术生成

线性基

参考资料

参考资料2

模板来源

概述

线性基是一种擅长处理异或问题的数据结构.设值域为[1, N N N],就可以用一个长度为 ⌈ log ⁡ 2 N ⌉ \lceil \log_2N \rceil log2N的数组来描述一个线性基。特别地,线性基第 i i i位上的数二进制下最高位也为第 i i i位。

性质

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

注意:性质1还有另一种说法:通过原集合S的某一个最小子集S1使得S1内元素相互异或得到的值域与原集合S相互异或得到的值域相同。

插入(初始化)

初始化即是将逐个元素插入线性基的过程.

由线性基的定义可知,一位上只有一个数字

我们考虑插入的操作,令插入的数为 x x x,考虑x的二进制最高位 i i i

  • 若线性基的第 i i i位为0,则直接在该位插入 x x x,退出;
  • 若线性基的第 i i i位已经有值 a i a_i ai,则 x = x ⊕ a i x = x\oplus a_i x=xai,重复以上操作直到 x = 0 x=0 x=0

如果退出时 x = 0 x=0 x=0,则此时线性基已经可以表示原先的 x x x了;反之,则说明为了表示 x x x,往线性基中加入了一个新元素。

void add(ll x) {
   
    for (int i = 50; i >= 0; --i) {
     
        if (x & (1LL << i)) {
    // x的最高位是1
            if (d[i]) {
     // 当前位置已经有线性基元素了,所以就异或一下
                x ^= d[i];
            } else {
     // 当前位置没有,就插入x,然后退出
                d[i] = x;
                break;
            }
        }
    }
}

验证

无法插入即不可表示.

bool check(ll x) {
   
    for (int i = 50; i >= 0; --i) {
   
        if (x & (1LL << i)) {
    // //注意,如果i大于31,前面的1的后面一定要加ll
            if (d[i]) {
    // 如果此位上已有元素,则异或一下
                x ^= d[i];
            } else {
    // 如果没有,则说明无法构成
                return false;
            }
        }
    }
    return true; // 可以构成
}

查询异或最小值

查询最小值相对简单一些.我们不难发现,在线性基的元素中,每个元素的最高位都是不同的(不会有两个线性基的元素的最高位相同).所以线性基中最小的元素,只要与其他元素异或,所得的结果必然比其本身大,因此,异或的最小值就是线性基的最小值(此处关于0的问题,看下文查询第k小.

查询异或最大值

这个算法相对固定.因为线性基中所有元素的最高位都不相同,所以可以从高到低逐步贪心获得.

ll cal() {
   
    ll ans = 0;
    for (int i = 50; i >= 0; --i) {
   
        if ((ans ^ d[i]) > ans) {
    // 只要能更新结果就更新
            ans = ans ^ d[i];
        }
    }
    return ans;
}

查询第k小

完整的说,应该是——从一个序列中取任意个元素进行异或,求能异或出的所有数字中第k小的那个。

这里就出现了一个问题.如果说原集合的元素个数是N,线性基的个数也是N,那么就一定无法凑出0,也就是线性基的最小值不是0,而是线性基中的最小值.

如果线性基的元素个数(下文称tot)小于N,那么最小值就是0.

在解决这个问题之前,我们对线性基进行一些预处理.我们将线性基元素都转化为形如 2 i 2^i 2i的形式,

void work()//处理线性基
{
   
    for (int i = 0; i <= 50; i++) {
   
        for (int j = 0; j < i
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值