線性基的學習+總結
阿新 • • 發佈:2020-07-29
線性基的學習+總結
參考部落格:線性基詳解
\(Summary:\)
線性基三大性質
- 原序列裡面的任意一個數都可以由線性基裡面的一些數異或得到
- 線性基裡面的任意一些數異或起來都不能得到 0
- 線性基裡面的數的個數唯一,並且在保持性質一的前提下,數的個數是最少的
- 一個序列的線性基不唯一,只是元素數量唯一而已。
線性基的構造:
\(add\) 函式 ,將一個數 \(x\) 插入線性基
void add(long long x) { tot = 1;//tot 表示線性基元素的數量 for (int i = 60; i >= 0; i--) { if(d[i]) tot++; if (x & (1ll << i)){ if (d[i]) x ^= d[i]; else { d[i] = x; break;//插入成功就退出 } } } }
如何求最大值
如何求在一個序列中,取若干個數,使得它們的異或和最大。
首先構造出這個序列的線性基,然後從線性基的最高位開始,假如當前的答案異或線性基的這個元素可以變得更大,那麼就異或它,答案的初值為 \(0\)。
long long MaxVal() {
long long ans = 0;
for (int i = 60; i >= 0; i--)//記得從線性基的最高位開始
if ((ans ^ d[i]) > ans)ans ^= d[i];
return ans;
}
如何求最小值
注意,這裡指的是用線性基內的元素能異或出的最小值。
顯然就是最小的 \(d[i]\)
如果是求整個序列能異或出的最小值
而不是這個序列的線性基能異或出的最小值的
話,要另外看一看有沒有元素不能插入線性基,如果有,那麼最小值就是 \(0\),否則依然是最小的 \(d[i]\)。
如何求第k小的值
從一個序列中取任意個元素進行異或,求能異或出的所有數字中第 \(k\) 小的那個。
void work()//處理線性基,使得對於 d[x]一定是第x位為1,可以異或成的最小值 { for (int i = 1; i <= 60; i++) for (int j = 1; j <= i; j++) if (d[i] & (1ll << (j - 1)))d[i] ^= d[j - 1]; //注意這個i也是從小到大列舉的,所以異或完之後,對於d[i]轉化成二進位制之後,如果低x位為1,那麼說明d[x]=0 } long long k_th(long long k) { if (k == 1 && tot < n)return 0;//特判一下,假如k=1,並且原來的序列可以異或出0,就要返回0,tot表示線性基中的元素個數,n表示序列長度 if (tot < n)k--;//類似上面,去掉0的情況,因為線性基中只能異或出不為0的解 work(); long long ans = 0; for (int i = 0; i <= 60; i++) { if (d[i] != 0) { if (k % 2 == 1)ans ^= d[i]; k /= 2; /* * 這裡解釋一下為什麼是這樣的 * 首先明確此時的線性基已經是最小的了 * 假設k的二進位制表示是 1101,那麼說明這個組成是第一個線性基存在,第二個不存在,第三個和第四個不存在 * 這樣表示的一定是第k個,前面就k-1個有:0000,0001,0010,0011,0100,0101,0110,0111,1000,1001,1010,1011,1100 */ } } }