BZOJ-4260: Codechef REBXOR(字典樹的應用)
題目描述
給定一個含 NNN 個元素的陣列 AAA,下標從 111 開始。請找出下面式子的最大值:(A[l1]⨁A[l1+1]⨁…⨁A[r1])+(A[l2]⨁A[l2+1]…⨁A[r2]),其中1≤l1≤r1<l2≤r2≤N 1\le l_1\le r_1<l_2\le r_2\le N1≤l1≤r1<l2≤r2≤N,x⨁yx\bigoplus yx⨁y 表示 xxx 和 yyy 的按位異或。
輸入格式
輸入資料的第一行包含一個整數 NNN,表示陣列中的元素個數。
第二行包含 NNN 個整數 A1,A2,…,AN。
輸出格式
輸出一行包含給定表示式可能的最大值。
樣例
樣例輸入
5
1 2 3 1 2
樣例輸出
6
樣例解釋
滿足條件的(l1,r1,l2,r2)(l1,r1,l2,r2)(l1,r1,l2,r2)有:(1,2,3,3),(1,2,4,5),(3,3,4,5)(1,2,3,3),(1,2,4,5),(3,3,4,5)(1,2,3,3),(1,2,4,5),(3,3,4,5)。
資料範圍與提示
對於 100100100 的資料,2≤N≤4×105,0≤Ai≤1092\le N \le 4\times 10^5, 0\le A_i\le 10^92≤N≤4×105,0≤Ai≤109。
思路:
1、要找出(A[l1]^A[l1+1]^…^A[r1])+(A[l2]^A[l2+1]…^A[r2]),只要找到兩個A[l]^…^A[r]就可以了。
2、利用字典樹可以尋找從1~n上最大的A[l]^…^A[r](1≤l≤r≤N)。
原理:比如一棵樹插入了2(010),5(101),7(111),分別查詢與6(110),3(011)做^運算結果的的最大數,
查詢時利用盡量走相反方向的字元指標的策略。
3、所以將問題轉化一下:
從左到右求1~n求最大的A[l]^…^A[r](1≤l≤r≤N)用l[i]表示,再從右到左求n~1的最大的A[l]^…^A[r](1≤l≤r≤N),用r[i]
表示。
最後求出max(l[i],r[i+1]);即為結果。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int N = 5e+5; const int Z = 2; int vc[N<<5][Z],l[N],r[N],a[N]; int tot; void it(int x) { int u=1,i; for(i=1<<30;i;i>>=1) { int c=(x&i)?1:0; if(!vc[u][c]) vc[u][c]=++tot; u=vc[u][c]; } } int f(int x) { int u=1,i,ans=0; for(i=1<<30;i;i>>=1) { int c=(x&i)?0:1; if(vc[u][c]) { u=vc[u][c]; ans+=i; } else u=vc[u][!c]; } return ans; } int main(void) { int now,n,i,ans; tot=1; memset(vc,0,sizeof(vc)); scanf("%d",&n); it(now=0); for(i=1;i<=n;i++) { scanf("%d",&a[i]); now^=a[i]; it(now); l[i]=max(l[i-1],f(now)); } tot=1; memset(vc,0,sizeof(vc)); it(now=0); for(i=n;i>=1;i--) { now^=a[i]; it(now); r[i]=max(r[i+1],f(now)); } ans=0; for(i=1;i<n;i++) ans=max(ans,l[i]+r[i+1]); printf("%d\n",ans); return 0; }