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的情况

转载于:https://www.cnblogs.com/zig-zag/archive/2013/05/07/3064064.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值