关于线性基的一些理解

这两天学了学线性基,觉得这个东西还挺有意思的,想发一下博客,讲一下自己的一些收获。。

学习参考:

ljh2000:http://www.cnblogs.com/ljh2000-jump/p/5869991.html

网上的神犇:http://blog.csdn.net/qaq__qaq/article/details/53812883

 

对于一个数集$V$,它的线性基$\beta$是它的一个子集,满足$\beta$中所有数互相异或得到的集合等价于$V$中所有数互相异或得到的集合。也就是说,$\beta$可以看成是$V$的压缩。

 

线性基有一些性质:

1.线性基的异或集合中不存在0。也就是说,$\beta$是$V$中线性无关的极大子集。(这些概念以后再补吧。。)

2.线性基中每个元素的异或方案唯一,也就是说,线性基中不同的异或组合异或出的数都是不一样的。这个与性质1其实是等价的。

3.线性基的二进制最高位互不相同。

4.如果线性基是满的,它的异或集合为$[1,2^{n}-1]$。

5.线性基中元素互相异或,异或集合不变。

 

那么,我们如何维护一个线性基呢?

 

插入

插入很简单,当我们插入一个数时,从高位到低位依次枚举当前线性基的每个元素。

如果我们插入的数x&当前位为1,那么我们分情况讨论:

如果线性基当前位没有元素,那么把当前位赋为x,并break;否则x^线性基当前位。

那么我们可以发现,插入x的最终结局是:要么x被选入线性基中;要么x最后变成了0,说明x已经可以通过线性基中的元素异或出来了。

1 il void add(RG ll x){
2     for (RG ll i=62;i>=0;--i)
3     if (x>>i&1){
4         if (!p[i]){ p[i]=x; break; }
5         x^=p[i];
6     }
7     return;
8 }

合并线性基与插入类似,将另一个线性基暴力插入一个线性基即可。

 

查询存在性

相当于插入的操作,如果x最后变成了0,说明x已经存在于线性基的异或集合中了。

 

查询异或集合中最大值

从高位到低位扫描。如果当前res^p[i]能使得答案变大,那么就异或。最后得到的res就是线性基异或集合中的最大值。

1 il ll getmax(){
2     RG ll res=0;
3     for (RG ll i=62;i>=0;--i)
4     if (res<(res^p[i])) res^=p[i];
5     return res;
6 }

 

查询异或集合中最小值

最小值就是线性基中最低位的数。

 

查询异或集合中k小值

我们考虑改造一下线性基,使得每一位互相独立。

如果j<i,且p[i]的第j位是1,就把p[i]^p[j]。

这样,对于二进制的每一位i。只有p[i]这一位是1,其他的都是0。

同样,根据性质5,这个线性基的本质也是没有改变的。

我们查询的时候,将k进行二进制拆分,如果第i位是1,就异或上线性基中第i个元素,最终得出的答案就是k小值(这个贪心该怎么证呢。。)

 1 il void rebuild(){
 2     for (RG ll i=62;i>=0;--i)
 3     for (RG ll j=i-1;j>=0;--j)
 4         if (p[i]>>j&1) p[i]^=p[j];
 5     for (RG ll i=0;i<=62;++i) if (p[i]) d[cnt++]=p[i];
 6     return;
 7 }
 8 
 9 il ll query_kth(RG ll k){
10     if (k>=(1LL<<cnt)) return -1; RG ll res=0;
11     for (RG ll i=62;i>=0;--i)
12     if (k>>i&1) res^=d[i];
13     return res;
14 }

 

线性基大概就是这些操作吧。。感觉自己还不是很理解其中的一些操作,不过光是记下来还是很容易的。。

posted @ 2017-03-14 14:26  wfj_2048  阅读(3486)  评论(1编辑  收藏  举报