1. 程式人生 > >#114. k 大異或和

#114. k 大異或和

題目描述

這是一道模板題。

給由 n n n 個數組成的一個可重集 S S S,每次給定一個數 k k k,求一個集合 T⊆S T \subseteq S T⊆S,使得集合 T T T 在 S S S 的所有非空子集的不同的異或和中,其異或和 T1xorT2xor…xorT|T| 是第 k k k 小的。
輸入格式

第一行一個數 n n n。
第二行 n n n 個數,表示集合 S S S。
第三行一個數 m m m,表示詢問次數。
第四行 m m m 個數,表示每一次詢問的 k k k。
輸出格式

輸出 m m m 行,對應每一次詢問的答案,第 k k k 小的異或和。如果集合 S S S 的所有非空子集中,不同的異或和數量不足 k k k,輸出 −1 -1 −1。
樣例
樣例輸入

3
1 2 3
5
1 2 3 4 5

樣例輸出

0
1
2
3
-1

資料範圍與提示

1≤n,m≤105,0≤Si≤250 1 \leq n, m \leq 10 ^ 5, 0 \leq S_i \leq 2 ^ {50} 1≤n,m≤10​5​​,0≤S​i​​≤2​50​​

大意:
在原有線性基上繼續構造,構造規則為
若i < j, aj的第j位是1,就把aj異或上ai。查詢的時候將k二進位制拆分,對於1的位,就異或上對應的線性基。
最終得出的答案就是k小值。

#include<iostream>
#include<cstdio>
#include<cmath> #include<algorithm> #include<cstring> using namespace std; const int maxn = 100000 + 5; const int maxm = 60;//最大50 const int top = 55; long long n,m; long long xxj[maxm]; long long a[maxn]; bool cmp(long long x,long long y) { return x > y; } long long ans; long long
all; long long full; bool zero; long long rank[maxn]; int main() { cin>>n; for(int i=1;i<=n;i++) scanf("%lld",&a[i]); sort(a+1,a+1+n,cmp); zero = 1; for(int i=1;i<=n;i++) { for(int j=top;j>=1;j--) { if((a[i] >> (j-1)) & 1) { if(xxj[j]) { a[i] ^= xxj[j]; if(a[i] == 0) zero = 1; } else{ xxj[j] = a[i]; break; } } } } for(int i=1;i<=top;i++) if(xxj[i]) { all ++; rank[all] = i; } for(int i=1;i<=all;i++) { full <<= 1; full += 1; } for(int i=top;i>=1;i--) { if(!xxj[i]) continue; for(int j=i - 1;j>=1;j--) { if((xxj[i] >> (j-1)) & 1) { xxj[i] ^= xxj[j]; } } } cin>>m; for(int i=1;i<=m;i++) { ans = 0; long long k; scanf("%lld",&k); if(zero) { k --; if(k > full) { printf("-1\n"); continue; } if(k == 0) { printf("%lld\n",ans); continue; } } if(k > full) { printf("-1\n"); continue; } for(int i=1;i<=top;i++) { if((k >> (i - 1)) & (long long)1) ans += xxj[rank[i]]; } printf("%lld\n",ans); } return 0; }