線性基詳解+例題

 

線性基

  • 何爲線性基?DEFINE:對於一個向量的集合,我們可以得到至少一個基向量集合,這個基向量集合就是線性基。線性基內的一個或多個基向量進行異或操作總能得到原向量集合中的向量。

性質

  1. 原集合中的任何一個數都可以由線性基中的一些數異或得到
  2. 線性基中任意一些數異或都不能得到0
  3. 線性基大小唯一。也就是基向量的個數唯一。
  4. 一個集合可以有多個大小相同但基向量不完全相同的線性基。

如何得到線性基?

定義數組p[ i ]表示二進制第 i 位爲最高位的基向量。

我們需要得到一組基向量,類似線性代數中的階梯型矩陣。

舉個栗子

原集合:{7, 2, 3, 4} 集合向量對應二進制:

7:111

2:010

3:011

4:100

如何得到線性基?

(1)如果x的二進制最高位 i 對應的p[ i ]還沒有值,那麼插入p[ i ] = x

(2)如果x的二進制最高位 i 對應的p[ i ]已經被插入過了,那麼x ^= p[ i ]

【這樣做的目的就是爲了保證下三角爲0,保證我們的“矩陣”是個階梯型矩陣】

 

看上圖的線性基插入步驟

(1)遍歷7,7的最高位是第2位。插入,那麼第2位已經名花有主,p[2] = 7所以下面再插入的時候保證第2位爲0才行

(2)遍歷2,2的最高位是第1位。插入,那麼第1位也已經名花有主,p[1] = 2

(3)遍歷3,3的最高位是第1位。但是第1位已經名花有主了啊~於是我們將3異或p[1]也就是1. 那麼1的最高位是0,可以插入,於是p[0] = 1

(4)遍歷4,4的最高位是第二位。p[2] = 7了已經,所以我們看4的低位是不是可以插入呢?我們發現4的低位沒有爲1的位,於是4插入失敗。【這也證明了第二條性質】

 【可能我說的都是卵,只是說了插入的步驟,但是線性基的正確性顯然】

代碼

void add(ll x)
{
    for(int i = maxBit - 1; i >= 0; -- i )
    {
        if(x >> i & 1)
        {
            if(!p[i]) { p[i] = x; break; }
            x ^= p[i];
        }
    }
}

按照上述步驟得到線性基之後,我們可以對線性基進行進一步的操作:進行異或操作使得到的p[i]最小(也就是儘可能將低位的1異或成0),這樣我們可以保證線性基所有基向量異或起來可以得到最大的異或值

代碼

    for(int i = maxBit - 1; i >= 0; -- i )
        for(int j = i - 1; j >= 0; -- j)
            if(p[i] >> j & 1)
                p[i] ^= p[j];

 那麼如何得到第k小的異或值?

我們將線性基的基向量用Linear[ ]按照升序存儲。定義ans = 0,那麼我們從對k的每一個二進制位遍歷,如果爲1,那麼異或起來Linear[ i ]。【正確性顯然,可以舉例驗證】

Linear大小爲cnt,那麼可異或得到的數的個數爲2^cnt - 1【正確性顯然】(可以想下數學排列組合中學過的,cnt個數,每個數有兩種選擇,選/不選,所以一共是2^cnt種組合,但是這裏要求我們不能都不選,所以2^cnt - 1)

當然還有一點需要注意

如果線性基的大小等於原集合的大小,那麼原集合任意向量異或都不會得到0. 正確性也是顯然的【參考性質2】

所以在求第k小時,我們需要判斷是否可以得到0,如果可以得到0,0顯然是最小的,那麼我們就只需要找到第k-1小就可以了


P3812 【模板】線性基【XOR最大】

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
inline ll read()
{
    ll x = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
    while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

const int maxN = 55;
const int maxBit = 63;

ll n, a, p[maxBit];
ll cnt, Linear[maxBit];

void add(ll x)
{
    for(int i = maxBit - 1; i >= 0; -- i)
    {
        if(x >> i & 1)
        {
            if(!p[i]) {p[i] = x; break;}
            x ^= p[i];
        }
    }
}

ll solve()
{
    for(int i = maxBit - 1; i >= 0; -- i )
        for(int j = i - 1; j >= 0; -- j)
            if(p[i] >> j & 1)
                p[i] ^= p[j];
    cnt = 0;
    for(int i = 0; i < maxBit; ++ i )
        if(p[i]) Linear[cnt++] = p[i];
    ll ans = 0;
    for(int i = 0; i < cnt; ++ i )
        ans ^= Linear[i];
    return ans;
}

int main()
{
    n = read();
    for(int i = 0; i < n; ++ i )
        a = read(), add(a);
    printf("%lld\n", solve());
    return 0;
}

 [HDU 3949] XOR 【線性基_XOR第k小】

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
inline ll read()
{
    ll x = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
    while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

const int maxN = 55;
const int maxBit = 63;

ll n, a, p[maxBit];
ll cnt, Linear[maxBit];

void add(ll x)
{
    for(int i = maxBit - 1; i >= 0; -- i )
    {
        if(x >> i & 1)
        {
            if(!p[i]) { p[i] = x; break; }
            x ^= p[i];
        }
    }
}

void op()
{
    for(int i = maxBit - 1; i >= 0; -- i )
        for(int j = i - 1; j >= 0; -- j )
            if(p[i] >> j & 1)
                p[i] ^= p[j];
    cnt = 0;
    for(int i = 0; i < maxBit; ++ i )
        if(p[i]) Linear[cnt++] = p[i];
}

int main()
{
    int cas = 0;
    int TAT; cin >> TAT;
    while(TAT -- )
    {
        memset(p, 0, sizeof(p));
        n = read();
        for(int i = 0; i < n; ++ i )
            a = read(), add(a);
        op();
        printf("Case #%d:\n", ++cas);
        int QAQ; cin >> QAQ;
        while(QAQ -- )
        {
            ll k = read();
            if(cnt != n) -- k;
            if(k >= 1ll << cnt)
            {
                printf("-1\n");
                continue;
            }
            ll ans = 0;
            for(int i = 0; i < cnt; ++ i )
                if(k >> i & 1) ans ^= Linear[i];
            printf("%lld\n", ans);
        }
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章