看了无数人的博客,冥思苦想终于搞明白了,都是泪啊。
题意:给你一个序列
a
,然后让你计算有多少个三元组(i,j,
k),满足i
题解:字典树。
用字典树处理前k-1个数,当前是第k个数。
显然,我们要寻找的i,j需要满足的条件可以转化为:
Ai与Ak
的最高不同位t,而
Aj
的第t位与
Ai
的第t位相同。这样异或的结果就满足了要求。因为
Ai与Ak
第t位之前的一定是全部相同的,所以不论
Aj
选取什么数,都不会对更高位的比较有影响。
举个例子:当
it=0,kt=1
,此时
jt
必须满足等于0,才能使异或结果满足条件,并且j的更高位是无所谓的。
所以利用数组
cnt[31][2]
记录已插入的数字中每一位的0/1有多少个,num数组表示第K个数的每一位。在插入第K个数的时候,统计最高不同位,插入的第t位是
num[t]
,且他就是最高不同位,那它的同父节点的
1−num[t]
为根节点的树中所有节点均可成为i来寻找j,假设这个
1−num[t]
位根节点的树中有3个数字,就可以利用
C
但是对于插入的第t位来说,j不一定只在它的同父节点的子树里,因为j只需满足
jt
与
it
相同即可,所以它可以在第t位所在的这一层中与
it
相同的子树中就可以。但是其中也会有不合法的情况,因为i是必须小于j的,所以我们添加ext这个变量来记录不合法的情况。
对于ext这个变量,我们在处理完一个点的时候,我们可以将这个点作为i,来寻找有多少个点可以与这个点形成i,j的关系,记录下来,最后一起减去。(因为我们插入的是第K个数,所以它一定是大于所有已经插入的数的,所以这里找到的情况都是不合法的)。
我就卡在这个ext这里卡了好久。。。难过。。。。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=5e5+10;
int cnt[31][2],a[maxn],num[30];
struct bbq{
int nxt[2];
int cnt,ext;//cnt记录这个点被经过了几次。
};
bbq T[maxn*31];
int tsize;
long long ans;
void calc(int tmp,int cnt)
{
ans+=T[tmp].cnt*1ll*(T[tmp].cnt-1)/2;
ans+=(cnt-T[tmp].cnt)*1ll*T[tmp].cnt-T[tmp].ext;
}
void in()
{
int tmp=0;
for(int i=0;i<30;i++)
{
if(!T[tmp].nxt[num[i]])
T[tmp].nxt[num[i]]=++tsize;
if(T[tmp].nxt[1-num[i]])
{
calc(T[tmp].nxt[1-num[i]],cnt[i][1-num[i]]);
}
tmp=T[tmp].nxt[num[i]];
T[tmp].cnt++;
T[tmp].ext+=cnt[i][num[i]]-T[tmp].cnt;
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
memset(num,0,sizeof num);
memset(cnt,0,sizeof cnt);
memset(T,0,sizeof T);
ans=tsize=0;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
int temp=a[i];
for(int j=29;j>=0;j--)
{
num[j]=temp%2;
cnt[j][temp%2]++;
temp/=2;
}
in();
}
printf("%lld\n",ans);
}
}