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