题目描述
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}。
现在你需要知道你怎么选取需要融合的宝石,才能使生成的宝石能量密度最大。
输入
第一行,一个整数 n,表示宝石个数。
第二行, n个整数,分别表示a1至an,表示每颗宝石的能量密度,保证对于i ≠ j有 ai ≠ aj。
输出
输出一行一个整数,表示最大能生成的宝石能量密度。
样例输入
5
9 2 1 4 7
样例输出
14
题解
可持久化Trie树+STL-set
如果已经知道另外一个数可以出现的区间,那么很容易使用可持久化Trie树求出这个数与它的最大异或值。
所以我们只需要处理出每个数可能是哪些区间的次大值即可。
考虑将所有数从大到小排序,一个一个扔进set中。那么这里面所有的数都比当前的数大,在其中确定范围即可。
很容易想出某个数成为次小值的区间最大范围:最左端为 (前驱的前驱,后继) ,最右端为 (前驱,后继的后继) 。(这里都是开区间)
那么它们的并集就是 (前驱的前驱,后继的后继) ,即其中的所有数都可能出现在以当前数为次大值的区间中。
这样确定了区间以后,剩下的事情就交给可持久化Trie树就好了。按位从大到小无脑贪心即可。
需要注意一下set的边界问题,建议把0和n+1加入到set中防止越界,并减少判断的代码量。
#include <cstdio>
#include <algorithm>
#include <set>
#define N 100010
using namespace std;
struct data
{
int v , id;
}a[N];
set<int> s;
set<int>::iterator it;
int c[N * 30][2] , si[N * 30] , tot , root[N];
bool cmp(data a , data b)
{
return a.v > b.v;
}
int insert(int x , int v)
{
int tmp , y , i;
bool t;
tmp = y = ++tot;
for(i = 1 << 30 ; i ; i >>= 1)
t = v & i , c[y][t ^ 1] = c[x][t ^ 1] , x = c[x][t] , c[y][t] = ++tot , y = c[y][t] , si[y] = si[x] + 1;
return tmp;
}
int query(int x , int y , int v)
{
int ret = 0 , i;
bool t;
for(i = 1 << 30 ; i ; i >>= 1)
{
t = v & i;
if(si[c[y][t ^ 1]] - si[c[x][t ^ 1]]) ret |= i , x = c[x][t ^ 1] , y = c[y][t ^ 1];
else x = c[x][t] , y = c[y][t];
}
return ret;
}
int main()
{
int n , i , ans = 0 , l , r;
scanf("%d" , &n);
for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i].v) , a[i].id = i , root[i] = insert(root[i - 1] , a[i].v);
sort(a + 1 , a + n + 1 , cmp) , s.insert(0) , s.insert(n + 1);
for(i = 1 ; i <= n ; i ++ )
{
it = s.upper_bound(a[i].id) , it ++ ;
if(it == s.end()) r = n;
else r = *it - 1;
it -- , it -- ;
if(it == s.begin()) l = 1;
else l = *(--it) + 1;
s.insert(a[i].id);
if(i != 1) ans = max(ans , query(root[r] , root[l - 1] , a[i].v));
}
printf("%d\n" , ans);
return 0;
}