题意:给出一个序列,问有多少三元组满足(a[i]^a[j])<(a[j]^a[k])(i<j<k)
解题思路:可以枚举数字每一位考虑,建一棵记录前缀的字典树,sum记录这个节点添加的个数,x代表不符合要减去的个数,tot[i][j]代表第i位的j(j=0,1)当前数量。
枚举ak的每一位,对于ak的第t位(记作ak[t]),如果ak[t]==1,那么对答案产生贡献的ai[t]和aj[t]必然为0,而且ai[0]~ai[t-1]必然和ak[0]~ak[t-1]对应相等。记ak[t]对应地节点为pp,与其同父亲的另一个节点为p。
ai[0]~ai[t-1]和ak[0]~ak[t-1]和aj[0]~aj[t-1]全都相等时,ak[t]加入后,贡献为sum[p]*(sum[p]-1)/2
ai[0]~ai[t-1]和aj[0]~aj[t-1]有部分不相等,ai[0]~ai[t-1]和ak[0]~ak[t-1]全都相等时时,贡献为sum[p]*(tot[i][1-b[i]]-sum[p])-x[p],这时会出现不合法的情况,所以要减去
x[p]的话,每次加入一个点,那么这个点必然会在当前节点产生tot[i][b[i]]-sum[pp]不合法的贡献
如果ak[i]==0答案也类似可以得到
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <map> #include <set> #include <stack> #include <queue> #include <vector> #include <bitset> #include <functional> using namespace std; #define LL long long const int INF = 0x3f3f3f3f; int a,b[35],n; int cnt,sum[500009*35],nt[500009*35][2],tot[35][2]; LL ans,x[35*500009]; void Insert() { int k=0; for(int i=0; i<31; i++) { if(!nt[k][b[i]]) { nt[k][b[i]]=++cnt; memset(nt[cnt],0,sizeof nt[cnt]); x[cnt]=sum[cnt]=0; } int p=nt[k][1-b[i]],pp=nt[k][b[i]]; ans+=1LL*sum[p]*(sum[p]-1)/2; ans+=1LL*sum[p]*(tot[i][1-b[i]]-sum[p])-x[p]; x[pp]+=tot[i][b[i]]-sum[pp],sum[pp]++,tot[i][b[i]]++; k=nt[k][b[i]]; } } int main() { int t; scanf("%d",&t); while(t--) { scanf("%d",&n); cnt=ans=x[0]=sum[0]=0; memset(tot,0,sizeof tot); memset(nt[0],0,sizeof nt[0]); for(int i=1; i<=n; i++) { scanf("%d",&a); for(int j=30; j>=0; j--) b[j]=a&1,a>>=1; Insert(); } printf("%lld\n",ans); } return 0; }