G. (Zero XOR Subset)-less(线性基)
题目链接:http://codeforces.com/contest/1101/problem/G
题目大意:给你n个数,然后让你把这n个数分成尽可能多的集合,要求,每个集合的值看做这个集合所有元素的异或值,并且任意个集合对应的值,再进行异或也不能为0,然后如果不存在合理的分法的时候,输出-1。否则,输出能分出的最大的集合个数。
具体思路:求出这n个数的线性基就完事了,对于-1的情况,就是这n个值得异或值是0,这个时候无论你怎么分都是不管用的,其他情况直接输出线性基就可以了。
线性基的定义: 对于n个数,a1,a2,a3,a4.线性基b1,b2......,线性基满足的情况是这n个数,其中的任意个数的异或值都能用数组b中的某几个数求出来。
具体方法:一个数一个数的来,对于当前的数的首位(二进制),当前i位是1的时候,如果这一位没有被记录过,就让b[i]=a[i].否则,让a[i]^=b[i],继续往下走就可以了。
那么这个题为什么求线性基就可以了呢?我们可以通过求线性基的过程发现线性基的一个性质,线性基中的任意几个数都不可能异或是0,这个性质我们可以通过反证法来进行,如果异或是0的话,那就说明这个这几个数中,存在可以互相表达的情况,这就有违求线性基的过程了。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 # define ll long long 4 const int maxn = 2e5+100; 5 int a[maxn],p[100]; 6 void cal(int n) 7 { 8 for(int i=1; i<=n; i++) 9 { 10 if(a[i]==0)continue; 11 for(int j=63; j>=0; j--) 12 { 13 if((a[i]&(1<<j))==0) 14 continue; 15 if(p[j]==0) 16 { 17 p[j]=a[i]; 18 break; 19 } 20 a[i]^=p[j]; 21 } 22 } 23 } 24 int main() 25 { 26 int n; 27 scanf("%d",&n); 28 for(int i=1; i<=n; i++) 29 { 30 scanf("%d",&a[i]); 31 } 32 int t=a[1]; 33 for(int i=2; i<=n; i++) 34 { 35 t^=a[i]; 36 } 37 if(t==0) 38 printf("-1\n"); 39 else 40 { 41 cal(n); 42 int num=0; 43 for(int i=63;i>=0;i--){ 44 if(p[i])num++; 45 } 46 printf("%d\n",num); 47 } 48 return 0; 49 } 50