線性基求第k小異或值
阿新 • • 發佈:2019-03-14
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小異或值