1. 程式人生 > >線性基求第k小異或值

線性基求第k小異或值

ace http ble printf lin 題意 subset %d void

題目鏈接
題意:給由 n 個數組成的一個可重集 S,每次給定一個數 k,求一個集合 \(T \subseteq S\)
使得集合 T 在 S 的所有非空子集的不同的異或和中,
其異或和 \(T_1 \mathbin{\text{xor}} T_2 \mathbin{\text{xor}} \ldots \mathbin{\text{xor}}T_{|T|}\)是第 k 小的。


/*

1.照例建立線性基
2.使得線性基中有且只有base[i]的第i位為1
3.記錄所有有值的base[] 從低位到高位記為0~cnt,共cnt + 1個 (註:閉區間
這時線性基可以構成的數有(1 << cnt) + 1個,如果cnt + 1 < n的話 說明可以取零 這時可以構成的數有(1 << (cnt + 1))個
4.取 第k小 時
如果k大於可以構成的數的總數 那麽無解
否則res是所有base[i] ((k - 1)的第i位為1) 的異或和 

*/

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 55;
int n, m;

struct BASE{
    long long w[N];
    int cnt;
    void init(){
        memset(w, 0, sizeof(w));
    }
    void ins(long long x){
        for(int i = 50; i >= 0; --i){
            if((x >> i) & 1) 
                if(w[i]) x ^= w[i];
                else {w[i] = x; break;}
        }
    }
    void build(){
        for(int i = 50; i >= 0; --i){
            if(!w[i]) continue;
            for(int j = i + 1; j <= 50; ++j){
                if((w[j] >> i) & 1){
                    w[j] ^= w[i];
                }
            }
        }
        //for(int i = 0; i <= 5; ++i) printf("%d %lld\n", i, w[i]);
        for(int i = 0; i <= 50; ++i){
            //printf("%d %lld\n", i, w[i]);
            if(w[i]){
                w[cnt++] = w[i];
            //  printf("%d %lld\n", cnt - 1, w[cnt - 1]);
            }
        }
        --cnt;
    }
    long long query(long long x){
        long long res = 0;
        for(int i = cnt; i >= 0; --i){
            if((x >> i) & 1) res ^= w[i];
        }
        return res;
    }
}base;

int main() {
    base.init();
    scanf("%d", &n);
    long long x;
    for(int i = 1; i <= n; ++i){
        scanf("%lld", &x);
        base.ins(x);
    }
    base.build();
    scanf("%d", &m);
    for(int i = 1; i <= m; ++i){
        scanf("%lld", &x);
        if(n != base.cnt + 1) --x;//註意是非空子集 所以特判可否取零 
        if(x >= (1ll << (base.cnt + 1))) printf("-1\n"); //這種情況下無法取到 
        else printf("%lld\n", base.query(x));
    }
    return 0;    
} 

線性基求第k小異或值