hdu 3949 第k大异或组合
题意:
给你一些数,其中任选一些数(大于等于一个),那么他们有一个异或和。
求所有这样的异或和的第k小。
我们可以将每一位看成一维,然后就是给我们n个60维的向量,求它们线性组合后得到的向量空间中,第k小的向量。
因为给我们的向量不一定是非线性相关的(即存在一些向量可以被其他向量线性表示出),所以我们先进行异或高斯消元,将这n个数”精简出“一组基底,即精简前得到的向量空间和精简后的到的是一样的。(精简后最多有60个向量)。
假如我们的到了m个基底,因为它们线性不相关,所以我们有2^m种可能(包括0)。
高斯消元以后,我们得到的每个数的最高位非零位一定不为相同,我们按照从高位到低位的顺序排序(高斯消元后本来就是这个顺序),并且从上到下,遍历每一个数,并且用他将它上面的数的它的最高位去掉(如果本来就是0就不用),这样就可以证明”能加则加“了。
然后就二分啦。。。。。
1 #include <cstdio> 2 #include <iostream> 3 #define N 10010 4 using namespace std; 5 6 typedef long long dnt; 7 8 int n, m, q; 9 dnt aa[N]; 10 int bit[N]; 11 bool flag; 12 13 void gauss() { 14 int i=0; 15 for( int b=60; b>=0 && i<n; b-- ) { 16 for( int j=i; j<n; j++ ) { 17 if( (aa[j]>>b)&1 ) { 18 swap( aa[i], aa[j] ); 19 break; 20 } 21 } 22 if( (aa[i]>>b)&1 ) { 23 bit[i] = b; 24 for( int j=i+1; j<n; j++ ) 25 if( (aa[j]>>b)&1 ) aa[j] ^= aa[i]; 26 i++; 27 } 28 } 29 m = i; 30 for( i=0; i<m; i++ ) { 31 for( int j=i-1; j>=0; j-- ) { 32 if( (aa[j]>>bit[i])&1 ) 33 aa[j] ^= aa[i]; 34 } 35 } 36 flag = m<n; 37 } 38 dnt query( dnt k ) { 39 if( !flag ) k++; 40 if( k>(1LL<<m) ) return -1; 41 dnt cur = 0; 42 for( int i=0; i<m; i++ ) { 43 if( k>(1LL<<(m-1-i)) ) { 44 k-=(1LL<<(m-1-i)); 45 cur ^= aa[i]; 46 } 47 } 48 return cur; 49 } 50 int main() { 51 int T; 52 scanf( "%d", &T ); 53 for( int cas=1; cas<=T; cas++ ) { 54 scanf( "%d", &n ); 55 for( int i=0; i<n; i++ ) 56 scanf( "%lld", aa+i ); 57 gauss(); 58 scanf( "%d", &q ); 59 printf( "Case #%d:\n", cas ); 60 for( int i=0; i<q; i++ ) { 61 dnt k; 62 scanf( "%lld", &k ); 63 printf( "%lld\n", query(k) ); 64 } 65 } 66 }