線性基學習筆記
阿新 • • 發佈:2020-11-04
簡介
線性基是一種擅長處理異或問題的資料結構。設值域為\([1,N]\),就可以用一個長度為\(\lceil \log_2N \rceil\)的陣列來描述一個線性基。特別地,線性基第\(i\)位上的數二進位制下最高位也為第\(i\)位。
一個線性基滿足,對於它所表示的所有數的集合\(S\),\(S\)中任意多個數異或所得的結果均能表示為線性基中的元素互相異或的結果,即意,線性基能使用異或運算來表示原數集使用異或運算能表示的所有數。運用這個性質,我們可以極大地縮小異或操作所需的查詢次數。
插入和判斷
我們考慮插入的操作,令插入的數為\(x\),考慮\(x\)的二進位制最高位\(i\),
- 若線性基的第\(i\)
- 若線性基的第\(i\)位已經有值\(a_i\),則\(x = x\oplus a_i\),重複以上操作直到\(x=0\)。
如果退出時\(x=0\),則此時線性基已經可以表示原先的\(x\)了;反之,則說明為了表示\(x\),往線性基中加入了一個新元素。
很容易證明這樣複雜度為\(\log_2x\),也可以用這種方法判斷能否通過原數列異或得到一個數\(x\)。
程式碼:
void insert(ll x) { for (ll i = 55; i >= 0; i -- ) { if (x & (1ll << i)) { if (!b[i]) { b[i] = x; tot ++ ;//線性基的子集能異或出來的不同的數(包括0)的個數就是2^tot break; } else x ^= b[i]; } } return; } int check(ll x) { for(ll i = 55; i >= 0; i -- ) if (x & (1ll << i)) x ^= b[i]; return x == 0; }
查詢異或最值
查詢最小值相對比較簡單。考慮插入的過程,因為每一次跳轉操作,\(x\)的二進位制最高位必定單調降低,所以不可能插入兩個二進位制最高位相同的數。而此時,線性基中最小值異或上其他數,必定會增大。所以,直接輸出線性基中的最小值即可。
考慮異或最大值,從高到低遍歷線性基,考慮到第\(i\)位時,如果當前的答案\(x\)第\(i\)位為\(0\),就將\(x\)異或上\(a_i\);否則不做任何操作。顯然,每次操作後答案不會變劣,最終的\(x\)即為答案。
同樣,我們考慮對於一個數\(x\),它與原數列中的數異或的最值如何獲得。用與序列異或最大值類似的貪心即可解決。
程式碼:
ll qmax() { ll res = 0; for (ll i = 55; i >= 0; i -- ) res = max(res, res ^ b[i]); return res; } ll qmin() { ll res = 0; if (flag) return 0;//能得到0 for (ll i = 0; i <= 55; i ++ ) if (b[i]) return b[i]; }