「线性基」学习笔记

这里指OI里的线性基,用来解决与异或相关的问题。

线性基是一个整数集合对应的一个集合,其异或值域与原序列完全相同(不多不少)。所谓异或值域,就是在集合中选择任意子集的异或和的所有取值。这样一来,就异或和意义下,这两个集合就完全等效了。

异或的性质

交换律:$a \oplus b = b \oplus a$

结合律:$a \oplus (b \oplus c) = (a \oplus b) \oplus c$

自反性:$a \oplus a = 0$

异或可以理解为返回某两位是否不同,或者理解为不进位的加法。

构造线性基

当线性基内的元素的异或和能够表示出原集合内的每个元素时,它们的值域就一定相同了。因为考虑原集合中若干个元素的异或和时,只需要在线性基中选出对应的子集表示原集合被选中的每个元素然后异或起来就可以了。对于线性基内的一个元素被选多次的情况,根据自反性可以抵消。

假设要维护的数的二进制都是$L$位或更少的,我们考虑在线性基中维护$L$个元素,其中第$i$个是$i$位的(保证开头是1)。这样的话,线性基内的元素一定是互不相同的。

假设现在要往线性基里加入一个元素$x$。我们想在线性基里加上若干元素(或不变),使得从线性基里选一个子集能够表示出$x$。根据自反性,也就是要在线性基中找出一个子集使得其与$x$的异或和为0。也就是要选出几个元素抵消$x$。既然线性基内每一个的开头位都是不一样的,我们就很好判断——我们从高到低考虑$x$的第$i$位,如果是0那么必定不能选元素$i$(由于我们从高到低考虑,选$i$就永远无法抵消掉这一位);如果是1那么必须选元素$i$(由于我们从高到低考虑,不选$i$就永远无法抵消掉这一位),选了$i$之后,问题转化为$x$与这个元素异或之后的值$x'$与剩余元素的异或和要是0,肯定有$x>x'$,因此继续按相同的方法做就行了。如果发现某一位的线性基不存在,那么直接将其设为$x$就行了。挺天然的。

线性基内的元素一旦确定就不会修改了。

inline void insert(int x){
    for(int i = 50; i >= 0; --i){
        if(x & (1ll<<i)){
            if(!basis[i]){
                basis[i] = x;
                break;
            }
            x ^= basis[i];
            if(!x) break;
        }
    }
}

最大值

要求一个集合里元素的最大异或和,那么先建线性基。

线性基值域的最大值怎么求?从最高位到最低位贪心。先将$ans$设为0,然后从高到低去异或线性基里的元素。如果当前位是1那肯定要选,这个没问题,$ans= ans \oplus basis_i$;如果当前位是0呢?那就如果$ans \oplus basis_i > ans$则异或。因为无论如何第$i$位不能变成1,那么就先设法保证后面能更大。如果到了后面能把一位从0变成1,那肯定会去做;反之如果后面没这个能力,而却通过这一步使0变成1了,那肯定更优了。

值域大小

线性基中的元素各不相同,因此各子集的异或和也各不相同。答案是$2^{size}$。反证法——如果存在两个子集异或和相同,那么这两个子集异或和为0。通过结合律,我们知道如果从这两个子集中拿出一个元素$k$,其与剩余部分的异或和相同,也就是剩余部分能表示$k$,这个元素不必存在在线性基里。

 

posted @ 2019-06-24 14:33  DennyQi  阅读(314)  评论(0编辑  收藏  举报