Kanade's trio
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 1478 Accepted Submission(s): 548
Problem Description
Give you an array A[1..n],you need to calculate how many tuples (i,j,k) satisfy that (i<j<k) and ((A[i] xor A[j])<(A[j] xor A[k]))
There are T test cases.
1≤T≤20
1≤∑n≤5∗105
0≤A[i]<230
Input
There is only one integer T on first line.
For each test case , the first line consists of one integer n ,and the second line consists of n integers which means the array A[1..n]
Output
For each test case , output an integer , which means the answer.
Sample Input
1 5 1 2 3 4 5
Sample Output
6
题意:给你n个数,问你多少个三元组(i,j,k)满足a[i]^a[j]<a[j]^a[k]且i<j<k,其中^为异或。
思路:这个题与 CCPC-Wannafly Winter Camp Day7 (Div2, onsite) F 逆序对! (01字典树的O(nlogn)做法) 原理相似。
但是本题求的是三元组的个数,所以主要还是在于如何处理i<j<k。
考虑到使a[i]^a[j]<a[j]^a[k]得条件是 设x是a[i]与a[k]的最高不相同位,则a[j]的第x位与a[i]相同,与a[k]相反。
我们考虑从n到1倒着枚举a[i]。对于第x位,用sum[u]表示经过字典树上u节点的数作为a[k]时,下标比k小且在当前层第x位与a[k]相反的数字个数(即满足条件的a[j]的数量)。设num[u]为经过字典树上u节点的数字的个数,每次插入a[i]时,考虑它在第x层(第x位)产生的答案。
假设它在第y位为id(id=0或1),对应字典树上的节点l=ch[u][id],对偶节点r=ch[u][id^1],那么在当前位下标大于i的k有num[r]个,但是sum[u]中包含了下标小于等于i的a[j],于是设cnt[i][x][id]为前i个数中第x位为id的数字的个数,这些数中的每一个数(即a[j])都可以和a[i],num[r]中的每一个数(即a[k])组成一个只有下标不合法(j<=i<k)的满足异或要求的三元组(i,j,k)。因此直接从sum[r]中减去即可。
即若节点r存在,则对答案的贡献为ans+=sum[r]-num[r]*cnt[i][x][id];
在统计完第i个数的贡献值后,即可把他插入到字典树中,更新对应的num数组(每一个经过的节点值++)和sum数组(加上cnt[i-1][x][id^1],即加上这个数作为a[k]时候下标小于该数的a[j]的数量)。
需要注意初始化以及数组大小。
代码:
#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=5e5+5;
int n,m,k,q;
ll ans;
ll a[maxn];
ll sum[maxn*31];
int cnt[maxn][32][2],szz;
struct Trie
{
int ch[maxn*31][2];
int val[maxn*31];
int sz;
void init()
{
for(int i=0;i<=szz;i++) sum[i]=val[i]=0;
memset(ch[0],0,sizeof(ch[0]));
sz=0;
}
void add(int x,int idx)
{
int u=0;
for(int i=30;i>=0;i--)
{
int id=((x>>i)&1);
int l=ch[u][id],r=ch[u][id^1];
if(r) ans+=sum[r]-val[r]*(ll)cnt[idx][i][id];//
u=ch[u][id];
if(!u) break;//
}
u=0;
for(int i=30;i>=0;i--)
{
int id=(x>>i)&1;
if(!ch[u][id])
{
ch[u][id]=++sz;
memset(ch[sz],0,sizeof(ch[sz]));
val[sz]=0;
}
u=ch[u][id];//
sum[u]+=cnt[idx-1][i][id^1];//
val[u]++;//
}
}
}t;
int main()
{
int T,cas=1;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
t.init();
ans=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
for(int j=0;j<31;j++)
for(int k=0;k<2;k++)
cnt[i][j][k]=0;
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<31;j++)
{
cnt[i][j][0]=cnt[i-1][j][0];
cnt[i][j][1]=cnt[i-1][j][1];
cnt[i][j][((a[i]>>j)&1)]++;
}
}
for(int i=n;i>=1;i--) t.add(a[i],i);
printf("%lld\n",ans);
szz=t.sz;
}
return 0;
}

9万+

被折叠的 条评论
为什么被折叠?



