BZOJ-4260: Codechef REBXOR(字典树的应用)

题目描述

原题来自:CODECHEF September Challenge 2015 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;
}

猜你喜欢

转载自blog.csdn.net/qq_41829060/article/details/82821333