HDU 5269 ZYB loves Xor I (二分,字典树)

 

题意:给出一个序列,对每两个数求异或结果后取最低位的1出来作为一个数,然后求这些数字的和。比如:{a,b,c},结果是lowbit(a^b)+lowbit(a^c)+lowbit(b^a)+lowbit(b^c)+lowbit(c^a)+lowbit(c^b)。若不剔除结果为0的,应该有n*n个数的和作为结果。

思路:二分法/字典树法。

a^a肯定是0,不用考虑,也就是数本身不考虑,与其相等的数也不考虑。

按位对所有元素进行二分,比如第一位为0的有{xxx},第一位为1的有{yyy},然后对这两个序列再继续进行二分,也就是第一位为0第二位为0的有{xx},第一位为0第二位为1的有{x}...一直这样分下去。

在每次分完的时候,假设在第p层,那么两边在第p位上面分别是0和1,而前面p-1位全是相同的,则最低位不同就是第p位,所以结果应该是(2^p)*(左边个数)*(右边个数)。

可用递归实现,如果一边的个数为0或1的话,不用继续了,如果超过了29层,也不用继续了,他们肯定是相同的数字。所以最多为29层。每个数字在最坏情况下(即5万个数都相同)被遍历29次,那么就O(29*n),如果5万个都是不同的,那么就类似一颗满二叉树,更快。

 

 1 #include <bits/stdc++.h>

 2 #define LL long long

 3 using namespace std;

 4 const int N=50005;

 5 const int mod=998244353;

 6 int ans;

 7 void cal(deque<int> &tmp, int num)

 8 {

 9     deque<int> tmp0,tmp1;

10     int q=0;

11     while(!tmp.empty())

12     {

13         q=tmp.front();

14         tmp.pop_front();

15         if((q&(1<<num))>0)    tmp1.push_back(q);

16         else    tmp0.push_back(q);

17     }

18 

19     ans= (ans+(1<<num)*tmp1.size()*tmp0.size())%mod;

20     if(num>=28)    return;     //大于29位的认同为相同

21     if(!tmp1.empty()&&tmp1.size()>1)

22         cal(tmp1,num+1);

23     if(!tmp0.empty()&&tmp0.size()>1)

24         cal(tmp0,num+1);

25 

26 }

27 

28 int main()

29 {

30     //freopen("input.txt", "r", stdin);

31     int t, j=0, n, a;

32     deque<int> que;

33     cin>>t;

34     while(t--)

35     {

36         que.clear();

37         scanf("%d",&n);

38         for(int i=0; i<n; i++)

39         {

40             scanf("%d",&a);

41             que.push_back(a);

42         }

43         ans=0;

44         cal(que, 0);

45         printf("Case #%d: %d\n",++j,ans*2%mod);

46     }

47     return 0;

48 }

49 

50 AC代码
AC代码

 

你可能感兴趣的:(love)