题意:
给一个序列a1,a2,a3…………..an
让你寻找(i,j,k)对,满足ai oxr aj < aj oxr ak
主要是要想到用字母树去做。
我们将每个数插入到字母树中,然后每次查入的时候计算以当前插入的数为k的满足条件的(i,j,k)对有多少个。
但是题目要求了i < j < k
那么我们可以这样计算,将每个数拆成二进制位,我们匹配到了第w位,我们计算第w位的贡献,假设w=1,那么我们可以加上之前的前w-1位都匹配上了的w=0的数量乘以第w位为0的数量,这样就表明从第w位开始ak与前面的分离。这样的话,我们就计算多了一些,我们这样就计算了所有的i,j,k对满足ai oxr aj < aj oxr ak,但是i不一定在j前面。
现在的问题就变成了我们如何统计i不在j之前的满足ai oxr aj < aj oxr ak的对数。那么我们可以在进行匹配的时候顺便把这个维护了。
感觉我没有太讲清楚,具体的看代码吧。
就是那个sum维护那个不满足条件的。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn = 5e5+7;
template <typename T>inline void read(T &_x_){
_x_=0;bool f=false;char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') f=!f;ch=getchar();}
while ('0'<=ch&&ch<='9') {_x_=_x_*10+ch-'0';ch=getchar();}
if(f) _x_=-_x_;
}
struct node{
node*ch[2];
ll v,de;
node(){
v = 0,de=0;
ch[0] = ch[1] = NULL;
}
};
node*root,*rt;
ll val[maxn],sum[maxn][2];
ll ans=0;
void insert(int n){
rt=root;
for(int level=30;level>=0;level--){
int id=(n>>level)&1;
sum[level][id]++;
if(rt->ch[id]==NULL) rt->ch[id]=new node();
if(rt->ch[1-id]!=NULL){
ans+=(sum[level][1-id])*(rt->ch[1-id]->v);
ans-=rt->ch[1-id]->de;
}
rt=rt->ch[id];
rt->v++;
rt->de+=sum[level][id];
}
}
int main(){
ll t,n;
read(t);
while(t--){
ans=0;
memset(sum,0,sizeof(sum));
root=new node();
read(n);
for(int i=1;i<=n;i++){
read(val[i]);
insert(val[i]);
}
printf("%I64d\n",ans);
}
return 0;
}