线性基学习笔记

一堆参考资料

https://blog.sengxian.com/algorithms/linear-basis

https://oi.men.ci/linear-basis-notes/

线性基这玩意儿是用来解决异或问题一类手段

尽量说的通俗易懂一点好了

以下指的都是异或意义下的线性基,严谨的请看参考文献的第一个

概念

1.异或和

  一堆数字$a_1,a_2,a_3...a_n$的异或和为$a_1xora_2xora_3...xora_n$

2.线性相关

  若有一组数$a_1,a_2,a_3...a_n$,且存在某一个数是其他数的异或和,则称这一组数为线性相关,否则称其为线性无关

  更形象的说,如果存在某一个数能被其他数异或得到,那么这一组数就是线性相关

  比如说,$1,2,3$是线性相关,因为$1xor2=3$,但$1,2,4$是线性无关,因为不存在一个数能被其他数异或得到

3.张成

  一组数能够异或得到的所有数,成为这一组数的张成,记做$span(S)$(其中$S$是原数的集合)

  比方说,集合${1,2,4}$的张成是${1,2,3,4,5,6,7}$

4.线性基

  设$B$是$S$的的线性基,则$S$满足两个条件

    ①$Bin span(S)$,即$B$是$S$的张成的子集,更通俗的讲,就是$B$中的每一个数字都能被$S$中的数异或得到

    ②$B$是线性无关的

  然后线性基具有以下两个性质

    ①$S$能被$B$张成,也就是说$S$中的每一个数都能被$B$中的数表示出来

    ②$B$是$S$的极小生成集,也就是说只有$B$可以张成$S$,而$B$的任何真子集都不可以

    ③$S$中没有其他的线性无关集包含$B$作为真子集

构造

  将每一个数$p$从高位到低位扫(二进制意义下),如果第$i$位为$1$,若$a_i=0$,则令$a_i=p$并退出,否则令$p=p xor a$

  代码如下

1 inline void insert(ll x){
2     for(int i=63;i>=0;--i)//逆序枚举二进制位 
3     if(x>>i&1){//判断该位是否为1 
4         if(!b[i]) return (void)(b[i]=x);//如果b[i]为0,则令b[i]=x 
5         x^=b[i];//否则x=x xor b[i] 
6     }
7 }

  至于原因我就不说了(才不是因为我也不知道呢)

  不过需要注意的一点是,线性基的存储方式是矩阵,如果按上面的方法是把这个矩阵消成上三角矩阵,有的题目需要把它消成对角矩阵,到时候再说吧

  说人话的话,就是有的题目里这个板子不是这样写(很少),到时候再说好了

最大值

  要求一些数字能得到的最大异或和是多少

  可以考虑贪心,从高到低枚举每一位代表的线性基,如果异或上该位的线性基后答案增大,则异或

1 inline ll calc(){
2     ll ans=0;
3     for(int i=63;i>=0;--i)
4     if(ans^b[i]>ans) ans=ans^b[i];
5     return ans;
6 }

查询

  在线性基中可以查询一个数能否被异或得到

  具体的过程和插入类似,判断到了最后$x$是否变为0即可

1 inline bool find(ll x){
2     for(int i=63;i>=0;--i)
3     if(x>>i&1){
4         if(!b[i]) break;
5         x^=b[i];
6     }
7     return x!=0;
8 }

第$K$大

  线性基可用于判断一组数字异或得到的第$K$大是多少

  设$cnt$为线性基内数的个数(也就是$b[i]!=0$的$i$的个数),那么所有能表示出的数字共有$2^{cnt}$个(包括0)

  将$K$二进制拆分,如果$K$的第$i$位为1,则令$ans^=b[i]$

  注意了,这里得把矩阵给消成对角矩阵,也就是说板子要改一改,这个放到代码里说

  消成对角矩阵有一个好处,就是每一位上的1最多只会出现一次

  还有一个需要注意的地方,如果线性基里元素的个数等于原来元素的个数,那么说明这一组元素是线性无关的,那么他们无论怎么异或也得不到0。而我们之前的求法是默认能取到0的,所以这种时候得$++K$才行

  这里是板子线性基求第$K$大

后记

  然后简单的说一下有关线性基的应用之类的

  一般关于异或的题目都是要用到线性基的

  还有一些不是很明显的,比如有几个操作,两个操作一起搞相当于他们的异或。比如关灯,用一个二进制表示每个操作会关掉哪几盏灯,那么两个操作一起做的话关掉的就是这两个操作异或起来之后为1的那几盏

  还有,如果有一堆数,每个数有一个价值,要在满足取的数线性无关的前提下最大化价值,一般都是用贪心,下面的几道题里有。(具体证明我不会)

例题

洛谷P3857 [TJOI2008]彩灯

洛谷P4301 [CQOI2013]新Nim游戏

CF895C Square Subsets

洛谷P4570 [BJWC2011]元素

洛谷P3265 [JLOI2015]装备购买

洛谷P3292 [SCOI2016]幸运数字

洛谷P4151 [WC2011]最大XOR和路径

CF724G Xor-matic Number of the Graph

原文地址:https://www.cnblogs.com/bztMinamoto/p/9721149.html