線性基入門

簡介

線性基是一個集合php

從原集合中選取任意多個數異或獲得的值都能經過在線性基中選取一些數進行異或獲得;node

也就是說線性基是對原集合的壓縮c++

構建方法

首先,能夠知道ui

對於集合A = {a1,a2,...,an},將其中的ai(i∈[1,n])用ai ^ aj (j∈[1,n]且j≠i)替換獲得集合B = {a1,a2,...,ai-1,ai ^ aj,ai+1,...,an}
從集合A中選取任意多個數異或獲得的值都能經過在集合B中選取一些數進行異或獲得spa

證:code

從原集合A中選取一些數異或獲得排序

x = ak1 ^ ak2 ^ ... ^ akm (kj∈[1,n])ip

若是這些數中不包含ai,那麼這些數也在集合B中,x也能經過在B中取這些數異或獲得,get

若是包含ai,則ai能夠用(ai ^ aj ) ^ aj 替換,故x也能由集合B獲得input

因此

對於任意一個集合,若是其中有兩個元素的最高位相等,能夠用異或將其中一個元素替換,如此下去,可讓該集合用某一位做爲最高位的數惟一

代碼實現

線性基中不能含有0元素,若是插入一個元素會使線性基中存在零元素則單獨討論

struct node {
    ll b[65], p[65];
    int cnt, flag;
    node() {
        memset(p, 0, sizeof(p));
        memset(b, 0, sizeof(b));
        cnt = flag = 0;
    }
    //在線性基中插入一個元素
    bool insert(ll x) {
        for (int i = 62; i >= 0; i--) {
            if (x & (1LL << i)) {
                if (b[i]) {
                    x ^= b[i];
                }
                else {
                    b[i] = x;
                    return true;
                }
            }
        }
        flag = 1;
        return false;
    }
    //求異或最大值,即從高位向低位掃,若是能使答案更大就異或上該位的值
    ll qmax() {
        ll res = 0;
        for (int i = 62; i >= 0; i--) {
            if ((res ^ b[i]) > res) res ^= b[i];
        }
        return res;
    }
    //若是以前的插入出現了0,則返回0,不然返回線性基中最小的值
    ll qmin() {
        if (flag) return 0;
        for (int i = 0; i <= 62; i++) {
            if (b[i]) return b[i];
        }
        return 0;
    }
    //重構線性基,目的是爲了求異或第k小的數
    void rebuild() {
        for (int i = 62; i >= 1; i--) {
            if (b[i]) {
                for (int j = i - 1; j >= 0; j--) {
                    if (b[i] & (1LL << j)) b[i] ^= b[j];
                }
            }
        }
        for (int i = 0; i <= 62; i++) {
            if (b[i]) p[cnt++] = b[i];
        }
    }
    //異或第k小,重構以後的線性基第i位對名次的貢獻即爲(1 << i),從低到高看看k的二進制位第i位是否爲1,若爲1則要異或上i爲的值
    ll kth(ll k) {
        if (flag) --k;
        if (k == 0) return 0;
        ll res = 0;
        if (k >= (1LL << cnt)) return -1;
        for (int i = 0; i < cnt; i++) {
            if (k & (1LL << i)) res ^= p[i];
        }
        return res;
    }
};

線性基合併

暴力合併便可

node merge(node n1, node n2) {
    node res = n1;
    for (int i = 0; i <= 62; i++) {
        if (n2.b[i]) res.insert(n2.b[i]);
    }
    res.flag = n1.flag | n2.flag;
    return res;
}

例題

1、線性基求異或第K大

給定\(N\)(\(1<=N<=10000)\)個元素,\(Q\)(\(1<=Q<=10000)\)組詢問,每次詢問異或第k大是多少,不存在則輸出-1

題目連接:HDU-3949 XOR

題解

線性基裸題

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node {
    ll b[65], p[65];
    int cnt, flag;
    node() {
        memset(p, 0, sizeof(p));
        memset(b, 0, sizeof(b));
        cnt = flag = 0;
    }
    bool insert(ll x) {
        for (int i = 62; i >= 0; i--) {
            if (x & (1LL << i)) {
                if (b[i]) {
                    x ^= b[i];
                }
                else {
                    b[i] = x;
                    return true;
                }
            }
        }
        flag = 1;
        return false;
    }
    ll qmax() {
        ll res = 0;
        for (int i = 62; i >= 0; i--) {
            if ((res ^ b[i]) > res) res ^= b[i];
        }
        return res;
    }
    ll qmin() {
        if (flag) return 0;
        for (int i = 0; i <= 62; i++) {
            if (b[i]) return b[i];
        }
        return 0;
    }
    void rebuild() {
        for (int i = 62; i >= 1; i--) {
            if (b[i]) {
                for (int j = i - 1; j >= 0; j--) {
                    if (b[i] & (1LL << j)) b[i] ^= b[j];
                }
            }
        }
        for (int i = 0; i <= 62; i++) {
            if (b[i]) p[cnt++] = b[i];
        }
    }
    ll kth(ll k) {
        if (flag) --k;
        if (k == 0) return 0;
        ll res = 0;
        if (k >= (1LL << cnt)) return -1;
        for (int i = 0; i < cnt; i++) {
            if (k & (1LL << i)) res ^= p[i];
        }
        return res;
    }
};
node merge(node n1, node n2) {
    node res = n1;
    for (int i = 0; i <= 62; i++) {
        if (n2.b[i]) res.insert(n2.b[i]);
    }
    res.flag = n1.flag | n2.flag;
    return res;
}
int main() {
    int t;
    scanf("%d", &t);
    int cse = 0;
    while (t--) {
        int n;
        scanf("%d", &n);
        node lis;
        for (int i = 1; i <= n; i++) {
            ll x;
            scanf("%lld", &x);
            lis.insert(x);
        }
        cse++;
        printf("Case #%d:\n", cse);
        int m;
        scanf("%d", &m);
        lis.rebuild();
        for (int i = 1; i <= m; i++) {
            ll k;
            scanf("%lld", &k);
            printf("%lld\n", lis.kth(k));
        }
    }
    return 0;
}

2、BZOJ-2460 元素

Description

通過了大量的實驗後,著名法師 Dmitri 發現:若是給如今發現的每一種礦石進行合理的編號(編號爲正整數,稱爲該礦石的元素序號),那麼,一個礦石組合會產生「魔法抵消」當且僅當存在一個非空子集,那些礦石的元素序號按位異或起來爲零。 例如,使用兩個一樣的礦石必將發生「魔法抵消」,由於這兩種礦石的元素序號相同,異或起來爲零。
而且人們有了測定魔力的有效途徑,已經知道了:合成出來的法杖的魔力等於每一種礦石的法力之和。人們已經測定了現今發現的全部礦石的法力值,而且經過實驗推算出每一種礦石的元素序號。
如今,給定你以上的礦石信息,請你來計算一下當時能夠煉製出的法杖最多有多大的魔力。

Input

第一行包含一個正整數N,表示礦石的種類數。

接下來 N行,每行兩個正整數\(Number_i\)\(Magic_i\),表示這種礦石的元素序號和魔力值。

\(N<=1000,Number_i<=10^{18},Magic_i<=10^4\)

Output

僅包一行,一個整數:最大的魔力值

Sample Input

3 
1 10 
2 20 
3 30

Sample Output

50

題解

直接按val從大到小排序,優先插入val大的礦石,(最後線性基每一位都是固定的,因此優先插入val大的是最優的),成功插入統計答案便可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node {
    ll b[65], p[65];
    int cnt, flag;
    node() {
        memset(p, 0, sizeof(p));
        memset(b, 0, sizeof(b));
        cnt = flag = 0;
    }
    bool insert(ll x) {
        for (int i = 62; i >= 0; i--) {
            if (x & (1LL << i)) {
                if (b[i]) {
                    x ^= b[i];
                }
                else {
                    b[i] = x;
                    return true;
                }
            }
        }
        flag = 1;
        return false;
    }
    ll qmax() {
        ll res = 0;
        for (int i = 62; i >= 0; i--) {
            if ((res ^ b[i]) > res) res ^= b[i];
        }
        return res;
    }
    ll qmin() {
        if (flag) return 0;
        for (int i = 0; i <= 62; i++) {
            if (b[i]) return b[i];
        }
        return 0;
    }
    void rebuild() {
        for (int i = 62; i >= 1; i--) {
            if (b[i]) {
                for (int j = i - 1; j >= 0; j--) {
                    if (b[i] & (1LL << j)) b[i] ^= b[j];
                }
            }
        }
        for (int i = 0; i <= 62; i++) {
            if (b[i]) p[cnt++] = b[i];
        }
    }
    ll kth(ll k) {
        if (flag) --k;
        if (k == 0) return 0;
        ll res = 0;
        if (k >= (1LL << cnt)) return -1;
        for (int i = 0; i < cnt; i++) {
            if (k & (1LL << i)) res ^= p[i];
        }
        return res;
    }
};
node merge(node n1, node n2) {
    node res = n1;
    for (int i = 0; i <= 62; i++) {
        if (n2.b[i]) res.insert(n2.b[i]);
    }
    res.flag = n1.flag | n2.flag;
    return res;
}
const int N = 1050;
struct node1 {
    ll id, val;
    bool operator < (const node1 &b) const {
        return val > b.val;
    }
} a[N];
int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lld%lld", &a[i].id, &a[i].val);
    }
    sort(a + 1, a + n + 1);
    node lis;
    ll ans = 0;
    for (int i = 1; i <= n; i++) {
        if (lis.insert(a[i].id)) ans += a[i].val;
    }
    printf("%lld\n", ans);
    return 0;
}
相關文章
相關標籤/搜索