bzoj 3166 [Heoi2013]Alo 可持久化trie 线段树

先对序列建一个可持久化trie。
然后从大到小将元素插到线段树对应位置。
每次插入前求以该元素为此小值的最大答案
(求该元素在线段树中前驱的前驱+1和后继的后继-1,中间的部分与该元素的最大异或和就是答案)
最大异或和从可持久化trie上查询。

#include <bits/stdc++.h>
using namespace std;
#define N 51000
#define M 11000000
#define ls l,mid,now<<1
#define rs mid+1,r,now<<1|1
int n,ans;
int a[N],pos[N];
int cmp(int x,int y)
{return a[x]>a[y];}
struct trie
{
    int cnt;
    int ch[M][2],num[M],root[N];
    void insert(int &now,int pre,int v,int deep)
    {
        num[now=++cnt]=num[pre]+1;
        if(deep<0)return;
        ch[now][0]=ch[pre][0];
        ch[now][1]=ch[pre][1];
        if(v>>deep&1)insert(ch[now][1],ch[pre][1],v,deep-1);
        else insert(ch[now][0],ch[pre][0],v,deep-1);
    }
    int query(int now,int pre,int v,int deep)
    {
        if(deep<0)return 0;
        int t=v>>deep&1;
        if(num[ch[now][t^1]]-num[ch[pre][t^1]])
            return (1<<deep)+query(ch[now][t^1],ch[pre][t^1],v,deep-1);
        else return query(ch[now][t],ch[pre][t],v,deep-1);
    }
}tr1;
struct seg_tree
{
    int tr[N<<2],st[110],lp[110],rp[110],top;
    void insert(int l,int r,int now,int pos)
    {
        tr[now]++;
        if(l==r)return;
        int mid=(l+r)>>1;
        if(mid>=pos)insert(ls,pos);
        else insert(rs,pos);
    }
    void insert(int x){insert(0,n+1,1,x);}
    void get(int l,int r,int now,int lq,int rq)
    {
        if(lq<=l&&r<=rq)
        {
            st[++top]=now;lp[top]=l;rp[top]=r;
            return;
        }
        int mid=(l+r)>>1;
        if(mid>=lq)get(ls,lq,rq);
        if(mid<rq) get(rs,lq,rq);
    }
    int findl(int l,int r,int now)
    {
        if(l==r)return l;
        int mid=(l+r)>>1;
        if(tr[now<<1])return findl(ls);
        else return findl(rs);
    }
    int findl(int lq,int rq)
    {
        top=0;get(0,n+1,1,lq,rq);
        for(int i=1;i<=top;i++)
            if(tr[st[i]])
                return findl(lp[i],rp[i],st[i]);
    }
    int findr(int l,int r,int now)
    {
        if(l==r)return l;
        int mid=(l+r)>>1;
        if(tr[now<<1|1])return findr(rs);
        else return findr(ls);
    }
    int findr(int lq,int rq)
    {
        top=0;get(0,n+1,1,lq,rq);
        for(int i=top;i>=1;i--)
            if(tr[st[i]])
                return findr(lp[i],rp[i],st[i]);
    }
}tr2;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        tr1.insert(tr1.root[i],tr1.root[i-1],a[i],30);
        pos[i]=i;
    }
    sort(pos+1,pos+1+n,cmp);
    tr2.insert(0);tr2.insert(n+1);
    for(int i=1;i<=n;i++)
    {
        int t=pos[i];
        int l1=tr2.findr(0,t-1),r1=tr2.findl(t+1,n+1),l2=0,r2=n+1;
        if(l1==0&&r1==n+1)
        {tr2.insert(t);continue;}
        if(l1!=0)l2=tr2.findr(0,l1-1);
        if(r1!=n+1)r2=tr2.findl(r1+1,n+1);
        ans=max(ans,tr1.query(tr1.root[r2-1],tr1.root[l2],a[t],30));
        tr2.insert(t);
    }
    printf("%d\n",ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值