Light OJ 1269 字典树

http://lightoj.com/volume_showproblem.php?problem=1269

Little Jimmy is learning how to add integers. As in decimal the digits are 0 to 9, it makes a bit hard for him to understand the summation of all pair of digits. Since addition of numbers requires the knowledge of adding digits. So, his mother gave him a software that can convert a decimal integer to its binary and a binary to its corresponding decimal. So, Jimmy's idea is to convert the numbers into binaries, and then he adds them and turns the result back to decimal using the software. It's easy to add in binary, since you only need to know how to add (0, 0), (0, 1), (1, 0), (1, 1). Jimmy doesn't have the idea of carry operation, so he thinks that

1 + 1 = 0

1 + 0 = 1

0 + 1 = 1

0 + 0 = 0

Using these operations, he adds the numbers in binary. So, according to his calculations,

3 (011) + 7 (111) = 4 (100)

Now you are given an array of n integers, indexed from 0 to n-1, you have to find two indices i j in the array (0 ≤ i ≤ j < n), such that the summation (according to Jimmy) of all integers between indices i and j in the array, is maximum. And you also have to find two indices, p q in the array (0 ≤ p ≤ q < n), such that the summation (according to Jimmy) of all integers between indices p and in the array, is minimum. You only have to report the maximum and minimum integers.

Input

Input starts with an integer T (≤ 10), denoting the number of test cases.

Each case starts with a line containing an integer n (1 ≤ n ≤ 50000). The next line contains n space separated non-negative integers, denoting the integers of the given array. Each integer fits into a 32 bit signed integer.

Output

For each case, print the case number, the maximum and minimum summation that can be made using Jimmy's addition.

Sample Input

Output for Sample Input

2

5

6 8 2 4 2

5

3 8 2 6 5

Case 1: 14 2

Case 2: 15 1

题目大意:给出n个数字,求一段连续的区间[i,j],使得这段区间内的所有数的异或值最大,再求一段连续的区间[l,r],使得这段区间内所有数的异或值最小。输出最大值和最小值。

思路:01字典树,因为求的是区间异或值,因此需要前缀异或和数组,且Insert和query操作都是针对这个值来进行的。举个例子,设sum[1]=b[1],sum[2]=b[1]^b[2]、……那么插入的值依次为sum[1]、sum[2]……为什么要对前缀异或和数组进行操作呢?首先这样做可以取保得到的必定是一段连续的区间,且异或有这样的性质:a^a=0,a^b^a=0,所以结果也是没有问题的。注意我们要先进行查询操作再进行插入操作,不然的话如果先插入sum[i],那么再查询sum[i],无法得到正确的最小值(这样做得到的最小值必然是0 因为a^a=0)。进一步考虑到处理第一个数的时候,字典树中没有数据,给操作带来不便,因此我们可以预先插入一个数0,因为0^a=a,对结果没有影响。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#define INF 0x3f3f3f3f
using namespace std;

int tree[4000000][2];
int tot;
int a[32];
int b[50005];
int sum[50005];
int MMAX=0,MMIN=INF;

inline void to2(int n)
{
	memset(a,0,sizeof(a));
	int len=0;
	while(n>0)
	{
		a[len++]=n&1;
		n>>=1;
	}
}

inline void Insert()
{
	int root=0;
	for(int i=31;i>=0;i--)
	{
		int id=a[i];
		if(!tree[root][id])
			tree[root][id]=++tot;
		root=tree[root][id];
	}
}

inline void query()
{
	int MAX=0,MIN=0,root1=0,root2=0;
	for(int i=31;i>=0;i--)
	{
		int id=1-a[i];//异或
		if(tree[root1][id])//找最大值
		{
			MAX+=1<<i;
			root1=tree[root1][id];
		}
		else
			root1=tree[root1][1-id];
		if(tree[root2][a[i]])//找最小值
			root2=tree[root2][a[i]];
		else
		{
			MIN+=1<<i;
			root2=tree[root2][1-a[i]];
		}
	}
	MMAX=max(MMAX,MAX);
	MMIN=min(MMIN,MIN);
}

int main()
{
	int t;
	scanf("%d",&t);
	int times=0;
	while(t--)
	{
		int n;
		scanf("%d",&n);
		to2(0);
		Insert();//插入一个0
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&b[i]);
			sum[i]=sum[i-1]^b[i];
			to2(sum[i]);
			query();
			Insert();
		}
		printf("Case %d: %d %d\n",++times,MMAX,MMIN);
		memset(tree,0,(tot+1)*sizeof(tree[0]));
		MMAX=tot=0;
		MMIN=INF;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xiji333/article/details/88763716