题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5269
题意:给n个数,求所有的lowbit( a[i] xor a[j] )之和,i,j∈[1,n]
lowbit是一个数在二进制表示下的第一个1所在位的权值。
思路:首先是求所有点对,而且自己和自己算出来是0不用考虑,那么对于一个数,我们就只求这个数到它前面所有点的答案,最后再把答案乘2即可。
我们构建一棵trie树,因为数的范围小于2^29,我们将每一个数固定为30位二进制数插入trie树里面,并统计每个节点的经过次数。
这样,对于一个数x所贡献的答案,我们可以这样计算。枚举它的每一位p,假设存在一个数y,使得lowbit(x xor y) = 2^p ,那么x和y的前p-1位一定是一样的,然后我们查询第p位与x的第p位相异节点的经过次数num,第p位贡献的答案就是num*2^(p-1)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
#define LL long long
#define rep(i,j,k) for(int i = j; i <= k; i++ )
#define Rrep(i,j,k) for(int i = j; i >= k; i-- )
#define Clean(x,y) memset(x,y,sizeof(x))
#define mod 998244353
int T;
int n;
LL ans;
int len;
int root;
int Next[1000009][2];
int num[1000009];
void init()
{
root = 0;
len = 0;
Clean(Next,0);
Clean(num,0);
}
void insert(int root,int k)
{
int now = root;
rep(i,0,29)
{
int index = k & 1;
if ( !Next[now][index] ) Next[now][index] = ++len;
now = Next[now][index];
num[now]++;
k>>=1;
}
}
int query(int root,int k)
{
int now = root;
int temp;
int ans = 0;
int value = 1; //对应每一位上的权值
rep(i,0,29) //枚举每一位
{
temp = k & 1;
ans += value * num[Next[now][ 1 - temp ]]; //走与当前位相异的那个点计算答案
if ( Next[now][temp] == 0 ) break;
now = Next[now][temp];
value<<=1;
k>>=1;
}
return ans;
}
int main()
{
int temp;
cin>>T;
int kase = 0;
while(T--)
{
cin>>n;
ans = 0;
init();
while(n--)
{
scanf("%d",&temp);
insert(root,temp);
ans+=query(root,temp);
}
printf("Case #%d: ",++kase);
cout<<ans*2%mod<<endl;
}
return 0;
}