HDU 5269 ZYB loves Xor I Trie树
题目链接:
hdu:http://acm.hdu.edu.cn/showproblem.php?pid=5269
bc:http://bestcoder.hdu.edu.cn/contests/contest_chineseproblem.php?cid=603&pid=1002
题解:
(以下有提到位数的都是指二进制表示)
对于xor值为1的两个数,他们的最低位(二进制表示)必然不同,所以我们把n个数按最低位数不同分为两堆,这两堆个数的乘积就是xor的值等于1的贡献。同理,我们可以递归处理出xor值等于2,4,8,16...2^30的贡献值,然后把它们加起来。
一种做法是类似快速排序先按最低位数排序,0的在左边,1的在右边,算完2^0的贡献之后,再递归算全0的那块,全1的那块中2^1的贡献,依次类推。不过这种做法的缺陷是最坏情况下时间复杂度会变为n^2;
一种高效的做法是用一颗trie树来维护,把每个数从最低位开始按每一位的01取值存到trie树上,这样某一位为零的都会在左子树上,为1的都会在右子树上。这样离线处理好就不用我们每一次递归的时候去维护了.
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 6 const int maxn=50000+10; 7 const int mod=998244353; 8 typedef long long LL; 9 10 struct Trie{ 11 int ch[maxn*33][2],tot; 12 int val[maxn*33]; 13 void init(){ 14 val[0]=0; 15 memset(val,0,sizeof(val)); 16 memset(ch[0],0,sizeof(ch[0])); 17 tot=1; 18 } 19 void insert(int x){ 20 int p=0; 21 for(int i=0;i<30;i++){ 22 int tmp; 23 if((1<<i)&x) tmp=1; 24 else tmp=0; 25 if(!ch[p][tmp]){ 26 memset(ch[tot],0,sizeof(ch[tot])); 27 ch[p][tmp]=tot++; 28 } 29 p=ch[p][tmp]; 30 val[p]++; 31 } 32 } 33 void query(int cur,int h,LL &ans){ 34 int lef=ch[cur][0],rig=ch[cur][1]; 35 LL tmp=(LL)val[lef]*val[rig]%mod*(1<<h)%mod; 36 // printf("tmp:%d\n",tmp); 37 ans+=tmp; 38 ans%=mod; 39 if(lef) query(lef,h+1,ans); 40 if(rig) query(rig,h+1,ans); 41 } 42 }trie; 43 44 int n; 45 46 void init(){ 47 trie.init(); 48 } 49 50 int main(){ 51 int tc,kase=0; 52 scanf("%d",&tc); 53 while(tc--){ 54 init(); 55 scanf("%d",&n); 56 for(int i=0;i<n;i++){ 57 int x; 58 scanf("%d",&x); 59 trie.insert(x); 60 } 61 // for(int i=0;i<22;i++) printf("val:%d\n",trie.val[i]); 62 LL ans=0; 63 trie.query(0,0,ans); 64 printf("Case #%d: %lld\n",++kase,ans*2%mod); 65 } 66 return 0; 67 }