1. 程式人生 > 實用技巧 >線性基的學習+總結

線性基的學習+總結

線性基的學習+總結

參考部落格:線性基詳解

\(Summary:\)

線性基三大性質

  1. 原序列裡面的任意一個數都可以由線性基裡面的一些數異或得到
  2. 線性基裡面的任意一些數異或起來都不能得到 0
  3. 線性基裡面的數的個數唯一,並且在保持性質一的前提下,數的個數是最少的
  4. 一個序列的線性基不唯一,只是元素數量唯一而已。

線性基的構造:

\(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]\)

了,因為最小的 \(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
             */
        }
    }
}