這兩天學了學線性基,覺得這個東西還挺有意思的,想發一下博客,講一下自己的一些收獲。。
學習參考:
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 }
線性基大概就是這些操作吧。。感覺自己還不是很理解其中的一些操作,不過光是記下來還是很容易的。。