題目連結
題意:
有 n n個數和 q q個詢問,每個詢問給出一個正整數 k k,問這 n n個數異或組合出的所有數中第 k k小的是什麼數,若不存在則輸出 −1 -1。
思路:
首先構建出這 n n個數的異或線性基。
假設非零線性基共有 tot tot個,從小到大排列後,對於第 x x個線性基,前 1−(x−1) 1-(x-1)個線性基無論怎樣線性組合,其結果一定小於第 x x個線性基。
因為第 x x個線性基假設最高位的 1 1在第 k k位,因為異或不存在進位,故比它小的基無論怎麼組合都組合不出二進位位大於等於 k k的一個 1 1。
這樣的話,線性基的線性組合大小排序很類似於二進位的大小排序,比如:
10000 10000 一定比 01111,01101,01110 01111,01101,01110等等形如 0xxxx 0xxxx的位元大。
故可考慮對 k k二進位分解,若其第 i i位為1,則加上從小到大排序後第 i i小的線性基的貢獻。
故對於 tot tot個線性基,線性組合的情況一共有 2tot−1 2^{tot} - 1種。
但這樣並沒有包括 0 0的情況。
所以可以特判一下,若 n n個向量全部都用來構成線性基,則不會存在異或後為 0 0的情況。
故此題得解。
代碼:
#include<cstdio>#include<cstring>#include<algorithm>using namespace std;typedef long long ll;const int A = 1e4 + 10;ll a[A],b[110],c[A];int n,zero,tot;void init(){ zero = tot = 0; memset(b,0,sizeof(b)); for(int i=1 ;i<=n ;i++) for(int j=62 ;j>=0 ;j--){ if((a[i]>>j)&1){ if(b[j]) a[i] ^= b[j]; else{ b[j] = a[i];tot++; for(int k=j-1 ;k>=0 ;k--) if(b[k] && ((b[j]>>k)&1)) b[j] ^= b[k]; for(int k=j+1 ;k<=62;k++) if(((b[k]>>j)&1)) b[k] ^= b[j]; break; } } } zero = (tot<n);tot = 0; for(int i=0 ;i<=62 ;i++) if(b[i]) c[tot++] = b[i];}ll solve(ll k){ if(zero) k--; if(k >= (1LL<<tot)) return -1; ll ans = 0; for(int i=0 ;i<=62 ;i++) if((k>>i)&1) ans ^= c[i]; return ans;}int main(){ int T,_=1;scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1 ;i<=n ;i++) scanf("%I64d",&a[i]); init(); int q;scanf("%d",&q); printf("Case #%d:\n",_++); while(q--){ ll k;scanf("%I64d",&k); printf(