1. 程式人生 > >HDU 6059 Kanade's trio(字典樹)

HDU 6059 Kanade's trio(字典樹)

Description
給出一個長度為n的序列A[1]~A[n],求滿足i < j < k且(A[i]^A[j])<(A[j]^A[k])的三元組(i,j,k)個數
Input
第一行一整數T表示用例組數,每組用例首先輸入一整數n表示序列長度,之後輸入n個整數A[1]~A[n] (1<=T<=20,1<=sum{n}<=5e5,0<=A[i]<=2^30)
Output
對於每組用例,輸出滿足條件的三元組個數
Sample Input
1
5
1 2 3 4 5
Sample Output
6
Solution
對於任一對A[i],A[k],如果有滿足條件的A[j]存在,假設A[i],A[k]在第x位首次出現不同,那麼對於A[j],其前x-1位隨意,但第x位需要和A[i]相同,考慮列舉A[i],找合法的A[j]和A[k],因為要i < j < k,故把所有數字按編號從大到小插入到字典樹中,令cnt[i][j][k]為前i個數中在第j位是k的個數,sum[p]為經過字典樹中p節點的數作為A[k]時,在當前層滿足條件的A[j]個數,即A[j]的編號需要小於A[k]且在p節點所處層A[j]的值與A[k]的值不同,num[p]為經過字典樹p節點的數的個數,每次插入的數作為A[i]時,考慮其在字典樹第j層(即第j位)產生的答案,假設這個數在第j層值為v,所處節點為l,對偶節點為r,那麼在當前層與A[i]不同的A[k]有num[r]個,sum[r]表示以經過r的數作為A[k]滿足條件的A[j]的數量,但是這些A[j]中有不合法值即為那些編號小於等於i的A[j],這些不合法值共有cnt[i][j][v]個,與num[r]個A[k]組合產生cnt[i][j][v]*num[r]種不合法情況,進而sum[r]-cnt[i][j][v]*num[r]為當前插入的數作為A[i]時在第j層產生的答案數,在統計完後,要把這個數插入到字典樹中並更新num值和sum值
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const
int INF=0x3f3f3f3f,maxn=500001; int ch[maxn*30][2],tot,num[maxn*30],cnt[maxn][30][2],T,n,a[maxn]; ll ans,sum[maxn*30]; //num[i]表示當前經過字典樹的第i個節點的數的個數 //sum[i]表示當前字典樹第i個節點作為A[k]時,在當前層滿足條件的A[j]數(當前位與A[k]不同且編號小於A[k]的編號) //cnt[i][j][k]表示1~i中在第j位為k的數的個數 void Solve(int i) { int p=1; for(int j=29;j>=0;j--) { int
v=((a[i]&(1<<j))>0); int l=ch[p][v],r=ch[p][v^1]; if(r)ans+=sum[r]-(ll)cnt[i][j][v]*num[r];//(j<k)-(j<=i)=i<j<k if(!l)break; p=l; } p=1; for(int j=29;j>=0;j--) { int v=((a[i]&(1<<j))>0); if(!ch[p][v])ch[p][v]=++tot; p=ch[p][v]; num[p]++;//經過p節點的數字多了a[i] //與經過p節點的A[k](即插入的a[i])在第j層不同的A[j]需要編號小於i且在當前位於A[k]不同 sum[p]+=cnt[i-1][j][v^1]; } } int main() { scanf("%d",&T); while(T--) { for(int i=1;i<=tot;i++) { num[i]=sum[i]=0; for(int j=0;j<=1;j++)ch[i][j]=0; } for(int i=1;i<=n;i++) for(int j=0;j<=29;j++) for(int k=0;k<=1;k++) cnt[i][j][k]=0; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); for(int j=0;j<=29;j++) for(int k=0;k<=1;k++) cnt[i][j][k]=cnt[i-1][j][k]; for(int j=0;j<=29;j++) { int v=((a[i]&(1<<j))>0); cnt[i][j][v]++; } } tot=1;ans=0; for(int i=n;i>=1;i--)Solve(i); printf("%I64d\n",ans); } return 0; }