bzoj 3105

 

感觉这题出得真好。

我们将问题简化过后是这样的:

给定一个数集,找一个最大的非空子集(一个集合的大小是它的元素和)A,使得A不存在一个非空子集,其所有元素的异或和为0。

因为我们始终可以只选一个数,所以如果允许选空集,也没有选一个数优,所以我们将原来的问题变成:

给定一个数集,找一个最大的子集(一个集合的大小是它的元素和)A,使得A不存在一个非空子集,其所有元素的异或和为0。

我们定义拟阵(E,I),E为给定的数集,I的元素为所有满足“其所有元素异或和不为0”的集合加上空集。

我们可以将每个数看成一个向量,每一维是0或1,即对应的位,”所有元素异或和不为0“等价于这个向量集合线性不相关,

所以这是一个带权的向量拟阵。

我们先将所有数从大到小排序,从前往后每次尝试加入一个数,能加则加,能加的判定标准是加入后和当前已经加入的数线性不相关(在GF(2)域下?),即加入后不存在一个子集,其异或和为0。

判定是否存在一个子集异或和为0的方法具体看代码,有点类似高斯消元。

 1 /**************************************************************
 2     Problem: 3105
 3     User: idy002
 4     Language: C++
 5     Result: Accepted
 6     Time:8 ms
 7     Memory:1272 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #include <iostream>
12 #include <algorithm>
13 #define N 110
14 using namespace std;
15  
16 typedef long long dnt;
17  
18 int n;
19 int aa[N], bb[N];
20 int stk[N], top;
21 dnt ans;
22  
23 bool ok() {
24     for( int t=0; t<top; t++ ) 
25         bb[t] = stk[t];
26     for( int b=30,j=0; b>=0 && j<top; b-- ) {
27         for( int k=j; k<top; k++ ) {
28             if( (bb[k]>>b) & 1 ) {
29                 swap(bb[k],bb[j]);
30                 break;
31             }
32         }
33         if( (bb[j]>>b) & 1 ) {
34             for( int k=j+1; k<top; k++ )
35                 if( (bb[k]>>b) & 1 ) {
36                     bb[k] ^= bb[j];
37                     if( bb[k]==0 ) return false;
38                 }
39             j++;
40         }
41     }
42     return true;
43 }
44 int main() {
45     scanf( "%d", &n );
46     for( int i=0; i<n; i++ )
47         scanf( "%d", aa+i );
48     sort( aa, aa+n, greater<int>() );
49     dnt ans = 0;
50     for( int i=0; i<n; i++ ) {
51         stk[top++] = aa[i];
52         if( !ok() ) {
53             top--;
54             ans += aa[i];
55         }
56     }
57     printf( "%lld\n", ans );
58 }
59 
View Code

 

posted @ 2015-05-22 21:49  idy002  阅读(249)  评论(0编辑  收藏  举报