给定$N$个数,问它的任意子集异或产生的数进行排列,求第K小的数。
构造出线性基$B$后,如果$|B| < N$,那么代表N个数中有一个数是可以由线性基中的其他数异或出来的,那么相当于可以异或出$0$。也就是说这种情况下会多一个0作为最小数。
然后对于第$K$大,可以直接对$K$的二进制进行判断,如果为$1$就取线性基对应的数,然后进行异或就可以得出答案了。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 #define ll long long 5 #define Min(a,b) ((a)>(b)?(b):(a)) 6 #define Max(a,b) ((a)>(b)?(a):(b)) 7 const int maxn = 1e4 + 13; 8 const int maxl = 62; 9 ll a[maxn]; 10 11 struct LinerBasis 12 { 13 vector<ll> vec; 14 int n; 15 16 void build(ll *x, int n) 17 { 18 this->n = n; 19 vector<ll> a(maxl+1); 20 for(int i = 1; i <= n; i++) 21 { 22 ll t = x[i]; 23 for(int j = maxl; j >= 0; j--) 24 { 25 if( (t & (1ll<<j)) == 0 ) 26 continue; 27 if(a[j]) 28 t^=a[j]; 29 else 30 { 31 //将低位的已存在的a[k],异或消掉t的这一位1 32 //a[k]=0也没有影响 33 for(int k = 0; k < j; k++) 34 { 35 if(t & (1ll<<k)) 36 t ^= a[k]; 37 } 38 //消除高位上a[k]的第j位1 39 for(int k = j + 1; k <= maxl; k++) 40 { 41 if(a[k] & (1ll << j)) 42 a[k] ^= t; 43 } 44 a[j] = t; 45 break; 46 } 47 } 48 } 49 vec.clear(); 50 for(int i = 0; i <= maxl; i++) 51 { 52 if(a[i]) 53 vec.push_back(a[i]); 54 } 55 } 56 57 ll query(ll K) 58 { 59 //可能异或出0 60 if(vec.size() < n) 61 K--; 62 if(K > (1ll<<vec.size()) - 1) 63 return -1; 64 ll ans = 0; 65 for(int i = 0; i < vec.size(); i++) 66 { 67 if(K & (1ll << i)) 68 { 69 ans ^= vec[i]; 70 } 71 } 72 return ans; 73 } 74 75 }lb; 76 77 int main() 78 { 79 int T, Case = 0; 80 scanf("%d", &T); 81 while(T--) 82 { 83 int N, Q; 84 scanf("%d", &N); 85 for(int i = 1; i <= N; i++) scanf("%lld", &a[i]); 86 87 lb.build(a, N); 88 89 scanf("%d", &Q); 90 ll K; 91 printf("Case #%d:\n", ++Case); 92 while(Q--) 93 { 94 scanf("%lld", &K); 95 printf("%lld\n", lb.query(K)); 96 } 97 } 98 return 0; 99 }
原文:https://www.cnblogs.com/dybala21/p/11366699.html