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:
", cas );
60         for( int i=0; i<q; i++ ) {
61             dnt k;
62             scanf( "%lld", &k );
63             printf( "%lld
", query(k) );
64         }
65     }
66 }
View Code
原文地址:https://www.cnblogs.com/idy002/p/4524711.html