3166: [Heoi2013]Alo

50 篇文章 0 订阅
10 篇文章 0 订阅

3166: [Heoi2013]Alo

Time Limit: 20 Sec   Memory Limit: 256 MB
Submit: 841   Solved: 402
[ Submit][ Status][ Discuss]

Description

Welcome to ALO ( Arithmetic and Logistic Online)。这是一个VR MMORPG ,
如名字所见,到处充满了数学的谜题。
现在你拥有n颗宝石,每颗宝石有一个能量密度,记为ai,这些宝石的能量
密度两两不同。现在你可以选取连续的一些宝石(必须多于一个)进行融合,设为  ai, ai+1, …, a j,则融合而成的宝石的能量密度为这些宝石中能量密度的次大值
与其他任意一颗宝石的能量密度按位异或的值,即,设该段宝石能量密度次大值
为k,则生成的宝石的能量密度为max{k xor ap | ap ≠ k , i ≤ p ≤ j}。 
现在你需要知道你怎么选取需要融合的宝石,才能使生成的宝石能量密度最大。 

Input

第一行,一个整数 n,表示宝石个数。 
第二行, n个整数,分别表示a1至an,表示每颗宝石的能量密度,保证对于i ≠ j有 ai ≠ aj。 
 

Output

输出一行一个整数,表示最大能生成的宝石能量密度。 

Sample Input

5
9 2 1 4 7


Sample Output

14

HINT



【样例解释】 

选择区间[1,5],最大值为 7 xor 9。 

 

 

对于 100%的数据有 1 ≤ n ≤ 50000, 0 ≤ ai ≤ 10^9

Source

[ Submit][ Status][ Discuss]



xor最大值问题显然是可以用trie做的

假如我们能知道每个数字在哪些区间里,它都是次大值,那就用这些区间的数字组成的trie贪心一下就行了

主体思路就是维护区间的trie然后合并答案

维护区间trie可以用线段树套trie

那一个数字对于哪些区间是次大值?

对于一个数字,假设位置k

在k左边,找到最靠近k,且大于k的两个数字,同理在右边找两个

假设找到的每次找到的第二个分别i,j,那么区间(i,j)中任意数字,显然都有一种方法能弄进答案

找这个东西的话,,可以排序一下然后从大到小插入,用线段树搜索--

一开始是先找一个比他大的然后用比这个数字大的做第二个,WA了,,没想清楚--

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

const int maxn = 5E4 + 20;
const int T = 4;
const int N = 20;
const int M = 30;

struct data{
	int va,pos; data(){}
	data(int va,int pos): va(va),pos(pos){}
	bool operator < (const data &b) const {return va > b.va;}
}D[maxn];

int n,cnt,Root[maxn*T],ch[maxn*N*M][2],Num[M],a[maxn]
	,pre[maxn][2],nex[maxn][2],c[maxn*T];

void Change(int x)
{
	for (int i = 29; i >= 0; i--,x >>= 1) Num[i] = (x&1);
}

void Insert(int o,int l,int r,int pos)
{
	if (!Root[o]) Root[o] = ++cnt;
	for (int i = 0,x = Root[o]; i < 30; x = ch[x][Num[i]],i++) 
		if (!ch[x][Num[i]]) ch[x][Num[i]] = ++cnt;
	if (l == r) return;
	int mid = (l + r) >> 1;
	if (pos <= mid) Insert(o<<1,l,mid,pos);
	else Insert(o<<1|1,mid+1,r,pos);
}

int Query(int o,int l,int r,int ql,int qr)
{
	if (ql <= l && r <= qr) {
		int ret = 0,x = Root[o];
		for (int i = 0; i < 30; i++) {
			ret <<= 1;
			if (ch[x][Num[i]^1]) ret |= 1,x = ch[x][Num[i]^1];
			else x = ch[x][Num[i]]; 
		}
		return ret;
	}
	int mid = (l + r) >> 1,ret = 0;
	if (ql <= mid) ret = Query(o<<1,l,mid,ql,qr);
	if (qr > mid) ret = max(ret,Query(o<<1|1,mid+1,r,ql,qr));
	return ret;
}

void insert(int o,int l,int r,int pos)
{
	if (l == r) {++c[o]; return;}
	int mid = (l + r) >> 1;
	if (pos <= mid) insert(o<<1,l,mid,pos);
	else insert(o<<1|1,mid+1,r,pos);
	c[o] = c[o<<1] + c[o<<1|1];
}

int query(int o,int l,int r,int ql,int qr)
{
	if (ql > qr) return 0;
	if (l == r) return l;
	int mid = (l + r) >> 1,ret = 0;
	if (ql <= l && r <= qr) {
		if (c[o<<1|1]) return query(o<<1|1,mid+1,r,ql,qr);
		else if (c[o<<1]) return query(o<<1,l,mid,ql,qr);
		else return 0;
	}
	if (qr > mid && c[o<<1|1]) {
		ret = query(o<<1|1,mid+1,r,ql,qr);
		if (ret) return ret;
	}
	if (ql <= mid && c[o<<1]) ret = query(o<<1,l,mid,ql,qr);
	return ret;
}

void Work(int typ,int (*g)[2])
{
	memset(c,0,sizeof(c));
	for (int i = 1; i <= n; i++) D[i] = data(a[i],i);
	sort(D + 1,D + n + 1);
	for (int i = 1; i <= n; i++) {
		int pos = query(1,1,n,1,D[i].pos-1);
		if (pos) {
			g[D[i].pos][0] = pos;
			pos = query(1,1,n,1,pos-1);
			if (pos) g[D[i].pos][1] = pos;
		}
		insert(1,1,n,D[i].pos);
	}
	
	if (typ) {
		for (int i = 1; i <= n/2; i++) {
			int k = n - i + 1;
			for (int j = 0; j < 2; j++) {
				if (g[i][j]) g[i][j] = n - g[i][j] + 1;
				if (g[k][j]) g[k][j] = n - g[k][j] + 1;
				swap(g[i][j],g[k][j]);
			}
		}
		if (n&1) {
			int k = n/2 + 1;
			for (int j = 0; j < 2; j++) g[k][j] = n - g[k][j] + 1;
		}
	}
}

int main()
{
	//freopen("DMC.txt","r",stdin);
	cin >> n;
	for (int i = 1; i <= n; i++) {
		scanf("%d",&a[i]);
		Change(a[i]); Insert(1,1,n,i);
	}
	Work(0,pre);
	for (int i = 1; i <= n/2; i++) swap(a[i],a[n-i+1]);
	Work(1,nex);
	for (int i = 1; i <= n/2; i++) swap(a[i],a[n-i+1]);
	
	int Ans = 0;
	for (int i = 1; i <= n; i++) {
		int A = pre[i][0],B = nex[i][0];
		if (!A && !B) continue;
		if (!A) A = 1; else A = pre[i][1]?pre[i][1]+1:1;
		if (!B) B = n; else B = nex[i][1]?nex[i][1]-1:n;
		Change(a[i]); Ans = max(Ans,Query(1,1,n,A,B));
	}
	cout << Ans;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值