题目链接:Kanade’s trio
题意:给出一个序列A,问存在多少个不同的三元组满足
Ai xor Aj<Aj xor Ak (i<j<k)
题解:对于每一个数,我们把它转化成30位的二进制形式,并按照序列顺序依次插入到0/1字典树中,插入过程中,我们把当前插入的数作为
Ak
,每次计算其贡献加起来即为答案。
我们考虑三个数
a,b,c
。易得只有当
a
和
那么贡献共有3种,假设已经插入到了
Ak
的第
t
位:
(1).
(2).
Ai[1...t−1]≠Aj[1...t−1]
,记所有数中第
t
位为
(3).显然,在(2)中,存在不合法情况
Ai[1...t−1]≠Aj[1...t−1] and Aj[1...t−1]=Ak[1...t−1]
。所以我们要减去这一部分。而对于每一个节点
now
,其不合法贡献为
sum[now][Ak[t]]−cnt[son[now][Ak[t]]]
综上,我们把每一个节点前两种贡献加起来然后减去第三种贡献即为最终答案。
#include <bits/stdc++.h>
using namespace std;
const int N = 500005*30;
int a[N][30],sum[30][2],cnt[N],bs[N],n,id,k,st,t,d,dd;;
int main(){
int T;
scanf("%d",&T);
while(T--){
for(int i=0;i<=id;i++){
for(int j=0;j<30;j++)
a[i][j]=0;
cnt[i]=bs[i]=0;
}
for(int i=0;i<30;i++)
sum[i][0]=sum[i][1]=0;
id=0;
scanf("%d",&n);
long long ans=0;
for(int i=1;i<=n;i++){
scanf("%d",&k);
st=0;
for(int i=29;i>=0;i--){
t=(k>>i)&1;
if(!a[st][t])
a[st][t]=++id;
d=a[st][t];
dd=a[st][!t];
if(dd)
ans+=1LL*cnt[dd]*(cnt[dd]-1)/2+
1LL*cnt[dd]*(sum[i][!t]-cnt[dd])-bs[dd];
bs[d]+=sum[i][t]-cnt[d];
cnt[d]++;
sum[i][t]++;
st=d;
}
}
printf("%lld\n",ans);
}
return 0;
}
参考博客:
太阳星人FxxL