線性基(Linear Basis)學習筆記
阿新 • • 發佈:2022-01-06
前言
我看網路上沒有什麼非常系統的教學,可能是我太菜了吧,現在才學,做個記錄給自己看。
簡略介紹
一個數集能兩兩異或,能表出許多新的數。
線性基是一個集合,能夠在記錄最少的數的基礎上,表示出一個等價的異或集合。+
常用來解決最大異或子集問題。
下文假設 \(L\) 為值域最大值在二進位制下的位數。
構造方法 & 解決問題
插入
bool insert(ll val) { fd(i, L, 0) if (val >> i & 1)) { if (!b[i]) { // b[i] 是記錄線性基的陣列 b[i] = val; break; } val ^= b[i]; } return val; }
如果說 \(\text{val}\) 能被表出,那麼它一定會在最後變成 \(0\) 。否則,我們認定下標 \(i\) 的位置放置的數的二進位制下第 \(i\) 位一定是 \(1\)。並將它插入。
不難發現,一個插入的數被填進第 \(i\) 位時,其更高位一定被全部異或 \(0\) ,故 \(i\) 是它的最高位 \(1\) 的位置。記住這個性質,會在下面使用。
插入一個數複雜度是 \(O(L)\) 的。
最大異或子集
根據上面的性質,我們從高位貪心地考慮,希望能夠儘量讓高位的 \(1\) 能夠出現。
ll mx() { ll ret=0; fd(i, L, 0) if((ret ^ b[i]) > ret) ret ^= b[i]; return ret; }
\(O(L)\)。
合併兩個線性基
直接把一個線性基中的元素插入另一個,\(O(L^2)\) 。
求第 \(k\) 小能被表出元素
我們改造這個線性基,使得每一位相互獨立。類似高斯消元。從低位到高位消。
void rebuild() { fo(i, 1, L) fo(j, 1, i) if(d[i] >> (j - 1) & 1) d[i] ^= d[j - 1]; } ll k_th(ll k) { // 如果算上零的話需要有特判 if(k == 1 && tot < n) return 0;//特判一下,假如k=1,並且原來的序列可以異或出0,就要返回0,tot表示線性基中的元素個數,n表示序列長度 if(tot < n) --k;//類似上面,去掉0的情況,因為線性基中只能異或出不為0的解 // 記得先 rebuild ll ret = 0; fo(i, 1, L) if(d[i]) { if(k & 1) ret ^= d[i]; k >>= 1; } return ret; }
順便一提,實際上 \(\text{rebuild}\) 之後的線性基是完全等價的,可以正常做其他操作。
刪除
線上的做法太複雜了一般不考不是很優美,直接說離線吧。
線上性基的每一個位置維護一個最晚插入時間 \(t\) ,那麼插入的時候
FOR i=L~0
如果 目前這一位線性基為空
則將目前這一位的線性基附為 (v1,t1)
否則:
將目前這一位的線性基記為 (v2,t2)
如果 t2<t1:
將目前這一位的線性基替換為 (v1,t1)
v2^=v1
用(v2,t2)插入下一位線性基
否則:
v1^=v2
用(v1,t1)插入下一位線性基
在查詢的時候只需要看 \(t \ge t_0\) 的位置就好了。