hdu3949 xor
这个题是用来研究xor性质的一道好题。
首先我们可以暴力地找出些规律,我们发现不管拿出多少个数,他们能xor到的数,出现的次数都是一样的,并且都是2的倍数。事实上,我们不论用原数ai还是ai xor aj得到的数,去xor起来得到的数都是这些。
这样我们可以想方设法地化简我们用到的基数,也就是找到一组基数使得它们能够xor出所有原数能xor出的数,并且要尽量简单。我们可以模仿高斯消元的过程,就是把每个数当成一行二进制数去消元,然后保留一个类似上三角的东西,从高位向低位每一行保留连续的几个1,这些1行与行之间是不重复的,但每一行之中还会有额外的一些1,这些1是可重复的,就造成了一些数是xor不出来的。
自我感觉表述的好乱....
假设我们选出了m行,也就是m个基数,那么我们可以xor出2m个数,如果总共给你了n个数,那么就有n-m个数是可以被那些基数完全替代的,这些行在高斯消元的过程中就是自由变量。这些行的值为0,也就是它去xor任意一个数,原数都不会改变,这样就产生了2n-m个重复的数,这样便验证了我们一开始的结论。
其实这组基数还有一个很好的性质,就是xor出的第k大(去重)的数,恰好是k二进制分解后对应位上的基数xor起来的到的数。因为从上到下每个基数代表了从高到低的一些二进制位,那么我们就相当于用这些连续的二进制位去组合形成所有的数。
那对于这个题来说,我们要求第k大的数,我们把m行基数求出来了以后,选出k的各个二进制位对应的基数,xor起来就是答案。
xor
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #define maxn 22000 7 #define inf 1000000000 8 #define bit 63 9 using namespace std; 10 typedef unsigned long long LL; 11 LL a[maxn]; 12 int n,m,qu; 13 LL zi; 14 15 void gauss(int n) 16 { 17 int k=1; 18 for (int i=bit;i;i--) 19 { 20 int p=0; 21 for (int j=k;j<=n;j++) if ((a[j]>>(i-1))&1) { p=j; break; } 22 if (p) 23 { 24 swap(a[k],a[p]); 25 for (int j=1;j<=n;j++) if ((j!=k)&&((a[j]>>(i-1))&1)) a[j]^=a[k]; 26 k++; 27 } 28 } 29 m=k-1; 30 } 31 32 int main() 33 { 34 int cas,now=0; 35 scanf("%d",&cas); 36 while (cas--) 37 { 38 printf("Case #%d:\n",++now); 39 memset(a,0,sizeof(a)); 40 m=0; 41 scanf("%d",&n); 42 for (int i=1;i<=n;i++) scanf("%I64u",&a[i]); 43 gauss(n); 44 scanf("%d",&qu); 45 for (int i=1;i<=qu;i++) 46 { 47 scanf("%I64u",&zi); 48 if (m<n) //存在自由元 49 if (zi==1){ printf("0\n"); continue; }//rank1的是0 50 else zi--;//否则从1开始记 51 if (zi>=((LL)1<<m)) printf("-1\n");//除去0总共有2^m个 52 else 53 { 54 LL ans=0; 55 for (int i=1;i<=m;i++) 56 if ((zi>>(i-1))&1) ans^=a[m-i+1]; 57 printf("%I64u\n",ans); 58 } 59 } 60 } 61 return 0; 62 }
注意long long,注意0的情况
AC without art, no better than WA !