1. 程式人生 > >BZOJ-4260: Codechef REBXOR(字典樹的應用)

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≤l​1​​≤r​1​​<l​2​​≤r​2​​≤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×10​5​​,0≤A​i​​≤10​9​​。

思路:

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;
}