JZOJ3226. 【HBOI2013】ALO

3226. 【HBOI2013】ALO

Description

Welcome to ALO ( Arithmetic and Logistic Online)。这是一个VR MMORPG,如名字所见,到处充满了数学的谜题。

现在你拥有n颗宝石,每颗宝石有一个能量密度,记为ai,这些宝石的能量密度两两不同。现在你可以选取连续的一些宝石(必须多于一个)进行融合,设为ai, ai+1, …, aj,则融合而成的宝石的能量密度为这些宝石中能量密度的次大值与其他任意一颗宝石的能量密度按位异或的值的最大值,即,设该段宝石能量密度次大值为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

Data Constraint

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

Hint

样例解释:选择区间[1,5],最大值为7 xor 9。

Solution

题目大意

给定一个长度为n的数列,选定一个区间,以这个区间的次大值与区间内其他数异或,求最大值。

题解

我们可以先扫一遍,求出对于每一个位置 i ,在他前面比他大的两个最近的位置l1[i],l2[i], 和他后面的两个位置 r1[i],r2[i]
如图所示
然后,求区间 [l2[i]+1,r1[i]1] [l1[i]+1,r2[i]1] 的最大值。这个可以用可持久化trie树来实现。

SRC

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

#define N 50000 + 10
typedef long long ll ;
struct Trie {
    int ch[2] , tot , id ;
} s[50*N] ;

int a[N] , l1[N] , l2[N] , r1[N] , r2[N] , root[N] ;
int n , T ;
ll ans ;

void ADD( int &po , ll va , ll dig ) {
    s[++T] = s[po] ;
    po = T ;
    s[T].tot ++ ;
    if ( dig < 0 ) {
        s[T].id = va ;
        return ;
    }
    int son = ( ( va >> dig ) & 1 ) ;
    ADD( s[T].ch[son] , va , dig - 1 ) ;
}

void search( int l , int r , ll num , ll va , ll dig ) {
    if ( dig < 0 ) {
        ans = max( ans , num ) ;
        return ;
    }
    int son = ! ( ( va >> dig ) & 1 ) ;
    if ( s[s[r].ch[son]].tot - s[s[l].ch[son]].tot > 0 ) search( s[l].ch[son] , s[r].ch[son] , num + (1 << dig) , va , dig - 1 ) ;
    else search( s[l].ch[!son] , s[r].ch[!son] , num , va , dig - 1 ) ;
}

int main() {
    freopen( "ALO.in" , "r" , stdin ) ;
    freopen( "ALO.out" , "w" , stdout ) ;
    scanf( "%d" , &n ) ;
    for(int i = 1 ; i <= n ; i ++ ) scanf( "%d" , &a[i] ) ;
    l2[1] = 0 ;
    for (int i = 2 ; i <= n ; i ++ ) {
        l2[i] = i - 1 ;
        while ( l2[i] > 0 && a[l2[i]] < a[i] ) l2[i] = l2[l2[i]] ;
        l1[i] = l2[i] - 1 ;
        while ( l1[i] > 0 && a[l1[i]] < a[i] ) l1[i] = l2[l1[i]] ;
    }
    r1[n] = n + 1 ;
    for (int i = n - 1 ; i >= 1 ; i -- ) {
        r1[i] = i + 1 ;
        while ( r1[i] <= n && a[r1[i]] < a[i] ) r1[i] = r1[r1[i]] ;
        r2[i] = r1[i] + 1 ;
        while ( r2[i] <= n && a[r2[i]] < a[i] ) r2[i] = r1[r2[i]] ;
    }
    for (int i = 1 ; i <= n ; i ++ )
        ADD( root[i] = root[i-1] , a[i] , 32 ) ;
    for (int i = 1 ; i <= n ; i ++ ) {
        if ( l1[i] >= 0 && r1[i] <= n + 1 ) {
            search( root[l1[i]] , root[r1[i] - 1] , 0 , (ll)a[i] , 32 ) ;
        }
        if ( l2[i] >= 0 && r2[i] <= n + 1 ) {
            search( root[l2[i]] , root[r2[i] - 1] , 0 , (ll)a[i] , 32 ) ;
        }
    }
    printf( "%I64d\n" , ans ) ;
    return 0 ;
}

以上.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值