hdu 3949 XOR 线性基 第k小异或和

题目链接

题意

给定\(n\)个数,对其每一个子集计算异或和,求第\(k\)小的异或和。

思路

先求得线性基。

同上题,转化为求其线性基的子集的第k小异或和

结论

\(n\)个数的线性基为向量组\(B=\{b_0,b_1,b_2,...,b_t\}(有b_i[p_i]=1,p_1\lt p_2\lt ...\lt p_t)\),记\(k\)的二进制表示为向量\(\vec{K}\).

则第\(k\)小异或和为$$\oplus_{\vec{K}[i]=1}b_i$$

\(k\)的二进制表示中为\(1\)的那些位所对应的线性基中的向量异或起来的值。

正确性证明

对于任意的\(1\leq i\lt j\leq tot(tot\)为子集的总个数,也即异或和的总个数)

\(i\)的二进制表示为\(\vec{I}\)\(j\)的二进制表示为\(\vec{J}\),设从高到低的\(\vec{I}\)\(\vec{J}\)第一个不同的位为第\(pos\)位,因为\(i\lt j\),故有\(\vec{I}[pos]=0, \vec{J}[pos]=1\).

记第\(i\)小异或值为\(ii\),第\(j\)小异或值为\(jj\),对应的向量分别为\(\vec{II}, \vec{JJ}\). 根据上述构造第\(k\)小值的方法,构造\(\vec{II}\)时没有异或\(b_{pos}\),而构造\(\vec{JJ}\)时异或了\(b_{pos}\). 又由线性基的性质,只有\(b_{pos}[p_{pos}]=1\),故有\(\vec{II}[p_{pos}]=0, \vec{JJ}[p_{pos}]=1\).

\(\vec{II}\)\(\vec{JJ}\)高位都相同,第\(p_{pos}\)\(\vec{JJ}\)大,故\(\vec{II}\lt \vec{JJ}\),即\(ii\lt jj\).

所以\(i\lt j\rightarrow ii\lt jj\),所以\(rank(i)=rank(ii)\),得到了一一对应的关系,故构造的正确性得证。

注意点

如果原\(n\)个数表示成的\(01\)线性相关,那么除了可以用线性基线性组合而得的\(2^r-1\)个数外,另有最小的异或和为\(0\).

Code

#include <bits/stdc++.h>
#define maxl 60
#define LL long long
using namespace std;
struct LinearBasis {
    LL a[maxl+1]; bool rel; int sz;
    vector<LL> v;
    LinearBasis() { memset(a, 0, sizeof a); rel = false; sz = 0; v.clear();}
    void insert(LL t) {
        for (int i = maxl; i >= 0; --i) {
            if (!(t >> i & 1)) continue;
            if (a[i]) t ^= a[i];
            else {
                for (int j = 0; j < i; ++j) if (t >> j & 1) t ^= a[j];
                for (int j = i+1; j <= maxl; ++j) if (a[j] >> i & 1) a[j] ^= t;
                a[i] = t, ++sz;
                return;
            }
        }
        rel = true;
    }
    void basis() {
        for (int i = 0; i <= maxl; ++i) if (a[i]) v.push_back(a[i]);
    }
    LL kth(LL x) {
        LL ret = 0;
        for (int i = 0; i < v.size(); ++i) if (x >> i & 1) ret ^= v[i];
        return ret;
    }
};
int kas;
void work() {
    int n, q; LL x;
    scanf("%d", &n);
    LinearBasis lb;
    for (int i = 0; i < n; ++i) {
        scanf("%lld", &x);
        lb.insert(x);
    }
    lb.basis();

    scanf("%d", &q);
    printf("Case #%d:\n", ++kas);

    LL tot = (1LL << lb.sz) - 1;
    for (int i = 0; i < q; ++i) {
        scanf("%lld", &x);
        if (lb.rel) --x;
        if (x > tot) puts("-1");
        else printf("%lld\n", lb.kth(x));
    }
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}

posted @ 2017-11-07 20:32  救命怀  阅读(868)  评论(0编辑  收藏  举报