线性基学习笔记
事实证明在睡过头迟到的下午我可以学会以前学$n$次也不会的东西比如线性基什么的$qwq$
一个最最重要的性质
一个数列$\left \{ a_i \right \}$,它的子集异或起来能表示的数组成$\left \{ b_i \right \}$,令$c=a_i$^$a_j,a_i=c$,子集异或起来还是$\left \{ b_i \right \}$.
证明:对于$\left \{ b_i \right \}$中被表示不需要$a_i$的数,显然并不受影响.对于需要$a_i$但不需要$a_j$的数,因为$c$^$a_j=a_i$,所以也能够表示.对于既用到$a_i$又用到$a_j$的数,直接用$c$就好了.
对以上性质加以推广易知,集合中的任意一个数用这个数异或上其他数(随便几个)来替换,这个集合的自己异或起来表示的数组成的集合不变.再推广,集合中的随便哪些数,用原数异或上.....
简单总结就是集合里的数异或来异或去再替换替换(随便搞)这个集合的子集异或生成数的集合不会变.
线性基用来干啥 (简介
1.判断:子集异或起来能否表示$x$
2.求异或最大
3.求异或第$k$大
线性基是啥/构造线性基
一堆数字异或来异或去就是线性基
$p_i$表示最高位为$1$的是第$i$位的数.对于集合里的每个数,从高位到低位扫,假设现在是数$x$,扫到第$i$位.如果在此之前没有出现以第$i$位为最高位的数,那么就把这个数放进$p_i$,如果以前已经有了呢,那么就$x$异或$p_i$,然后继续往后扫.如果到最后也没找到一个$p_i$用来放$x$呢?这就说明$x$可以被表示.
就$OK$辣.为什么这样构造呢?首先是正确性,线性基的子集异或值和原集合是一样的,证明就是前面那个重要性质辣.然后是必要性,这也就是我先在前面放用来干啥的原因.因为这样我们保证了最高位为$i$位的数唯一,就可以按位操作了啊,按位判断,按位贪心什么的.
$Code$
inline void build() { go(i,1,n) yes(j,62,0) if(a[i]&&(1LL<<j)) { if(!p[j])p[j]=a[i]; else a[i]^=p[j]; } }
线性基用来干啥 (具体怎么做
1.判断:子集异或起来能否表示$x$
和线性基的构造类似(构造里不是有一种情况就是集合里的数可以被其他数表示嘛).从高位扫到低位,如果$x$在第$i$位上为$1$,$x$^$=p_i$.如果$x$可以变为$0$,那么就可以被表示.
inline bool sol(ll x) { yes(i,62,0)if(x&&(1LL<<i))x^=p[i]; if(!x)return 1;return 0; }
2.求异或最大
按位贪心.从高位往低位扫,如果异或$p_i$能使答案变得更大,就异或起来.
正确性:当前扫到第$i$位,前面产生的答案为$ans$.
若$ans$^$p_i>ans$,说明$ans$在第$i$位上为$0$,异或之后使得$i$位为$1$.那么不过第$i$位往后的数位上的值怎么变,异或后的$ans$一定是更优的(这一位变成$1$后面都是$0$和这一位是$0$后面全是$1$比较一下就知道了.)大概还有一个原因就是过了这村就没这店了,这次不把第$i$位变成$1$,以后就再没可能了.
若$ans$^$p_i<ans$,说明$ans$在第$i$位上为$1$,异或之后会使得第$i$为变成$0$.所以当然不要异或辣.理解和上一种情况差不多.
总结一下就是从高位到低位,能变成$1$就变成$1$,不需要考虑更低的位.
inline ll sol() { ll ret=0; yes(i,62,0)if((ret^p[i])>ret)ret^=p[i]; return ret; }
3.求异或第$k$大
解这个问题有点像数位$DP$的试填法.
首先要$rebuild$线性基,使得$p_i$只有第$i$位为$1$,其他位都是$0$.具体来说从大到小枚举$i$,再从$i-1$到$0$枚举$j$,遇到$p_i$的第$j$位为$1$,就$p_i$^$=p^j$.如果没有以第$j$位为最高位的数呢?对答案没影响.其实准确的描述$rebuild$应该是使得线性基中每一位为$1$的最多只有一个数.
如果当前位为$i,p_i!=0$,后面有$cnt$位$p_i$不为$0$,那么显然比$p_i$小的数有$2^{cnt}$个.后面就完全是试填法.为了方便起见,我们可以把不为$0$的 $p_i$按照$i$的从小到大顺序加入数组$q$.这样,而且会发现一个非常神奇的做法,就是如果$k$的第$i-1$位为$1$,那么就$ans$^$=q_i$,也就是$k$的从低到高第$i-1$位上是$0$还是$1$决定了选不选$q_i$.($i-1$是因为$q$数组下标c从$1$开始)很好想通叭$qwq$.
$Attention!!$要特判可以表示出$0$的情况!如果原集合可以表示出$0$,那么第$k$大就是除$0$外的第$k-1$大.怎么判断呢?看原集合的大小和线性基的大小就好了.如果线性基的大小小于原集合,那么就可以表示出$0$,(原集合有数可以被表示出,只要表示出这个数再异或这个数就是$0$)
inline ll sol(ll k) { ll ret=0; if(k>=(1LL<<ct))return -1;//判断无解情况 if(ct<n)k--;//判断0能否被表示 go(i,1,ct)if(k&&(1LL<<(i-1)))ret^=q[i]; return ret; }