hdu 6059

异或、字典树


题意

给定长度为n的序列A,求三元组的个数(i,j,k),满足A[i]^A[j] < A[j]^A[k]且i < j< k.

  • n<=5*10^5

题解

  • 动态地将二进制的数加入到字典树中,枚举k,此时1…k-1已经加入字典树
  • 然后枚举A[k]与A[i]的最高位不同的位置p,也就是加入字典树时,兄弟节点记录的个数num即为会产生贡献的A[i],那么会贡献num*tot[p][c],tot[i][p]表示前k-1个数中第i位为c的数的个数,0<=c<=1。
  • 但是此时不满足i<j,所以需要减去j<=i的情况,所以加入字典树时同时记录sg,表示不满足的情况产生的贡献,那么就是++tot[i][c]。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxnode 15000010
#define sigma_size 2
int tot[33][2];
ll ans;
struct Trie{
int ch[maxnode][sigma_size];
int num[maxnode],sz;
ll sg[maxnode];
void init(){
sz=1;
ch[0][0]=ch[0][1]=0;
num[0]=0;
sg[0]=0LL;
}
void insert(int *s){
int u=0;
for(int i=29;i>=0;i--){
int c=s[i];
if(!ch[u][c]){
ch[sz][0]=ch[sz][1]=0;
num[sz]=0;
sg[sz]=0LL;
ch[u][c]=sz++;
}
if(ch[u][1-c]) ans+=(ll)tot[i][1-c]*num[ch[u][1-c]]-sg[ch[u][1-c]];
u=ch[u][c];
tot[i][c]++;
num[u]++;
sg[u]+=tot[i][c];
}
}
};
Trie T;
int main(){
int ca;
scanf("%d",&ca);
while(ca--){
int n,v,pos[33];
scanf("%d",&n);
T.init();
ans=0;
memset(tot,0,sizeof(tot));
for(int i=0;i<n;i++){
scanf("%d",&v);
for(int j=0;j<30;j++)
pos[j]=v>>j&1;
T.insert(pos);
}
printf("%lld\n",ans);
}
return 0;
}
/*
1
5
1 2 3 4 5
*/