[BZOJ3166][HEOI2013]Alo-可持久化字典树

Alo

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

加强型数据By Hta


这道题题面和ALO除了名字外有哪怕半毛钱关系吗……
另外为什么加强数据了暴力还是跑得比正解快啊……


思路:
考虑到异或和最大,使用可持久化字典树。

考虑计算每一个点成为次大值时其所在区间的可行的左边最远位置和右边最远位置。
这两个位置分别是向左第二个比它大的数位置加一,和向右第二个比它大的数位置减一。

那么这个点对答案的贡献便是这个点与这段区间内任意一个数异或能得到的最大值。
那么如果得出这个范围,扫一遍并在字典树上查询即可。

考虑如何计算这个端点:
把所有位置按位置上的值从大到小排序,把这些位置依次插入一个set,那么当某一个位置被插入时,set中已有的所有位置上的值均比它大,那么对当前位置分别求两次前驱和后继即可得到这个位置上的数的合法区间。

然后就简单了~

#include<iostream>
#include<set>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x;
}

typedef pair<int,int> pr;
const int N=80009;
const int K=31;
const int M=N*K;

pr q[N];
set<int> s;
set<int>::iterator it;
int n,m,a[N],rt[N],l[N],r[N];
int ch[M][2],val[M],tot;

inline void chkmax(int &a,int b){if(a<b)a=b;}

inline void cp(int x,int y)
{
    val[y]=val[x];
    ch[y][0]=ch[x][0];
    ch[y][1]=ch[x][1];
}

inline int insert(int pre,int dep,int x)
{
    int now=++tot;
    cp(pre,now);
    val[now]++;
    if(!dep)return now;
    int nxt=(x>>dep-1)&1;
    ch[now][nxt]=insert(ch[pre][nxt],dep-1,x);
    return now;
}

inline int query(int tl,int tr,int x)
{
    int ret=0;
    for(int i=K-1;i>=0;i--)
    {
        int nxt=((x>>i)&1)^1;
        if(val[ch[tr][nxt]]-val[ch[tl][nxt]]>0)
            ret|=1<<i,tl=ch[tl][nxt],tr=ch[tr][nxt];
        else
            tl=ch[tl][nxt^1],tr=ch[tr][nxt^1];
    }
    return ret;
}

inline int las(int x)
{
    it=s.find(x);
    if(--it==s.end())return 1;
    if(--it==s.end())return 1;
    return (*it)+1;
}

inline int nxt(int x)
{
    it=s.find(x);
    if(++it==s.end())return n;
    if(++it==s.end())return n;
    return (*it)-1;
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        rt[i]=insert(rt[i-1],K,a[i]=read());
        chkmax(m,a[i]);q[i]=pr(a[i],i);
    }

    sort(q+1,q+n+1);
    for(int i=n;i>=1;i--)
    {
        s.insert(q[i].second);
        l[q[i].second]=las(q[i].second);
        r[q[i].second]=nxt(q[i].second);
    }

    int ans=0;
    for(int i=1;i<=n;i++)
        if(a[i]!=m)
            chkmax(ans,query(rt[l[i]-1],rt[r[i]],a[i]));
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值