题解 洛谷 P4098 【[HEOI2013]ALO 】

考虑原序列中的每一个值作为构成最终答案的那个次大值,那么其所在的合法区间最大时,其对答案的贡献最大。

一个值作为最大值时有两个合法的最大区间,一个是左边第二个比其大的位置和右边第一个比其大的位置构成的区间,另一个是左边第一个比其大的位置和右边第二个比其大的位置构成的区间,这两个区间都是开区间。确定区间可以从小到大排序,用双向链表一个一个删除即可。

然后就将问题简化了,现在要解决给定一个值,求给定区间与其的异或最大值,可以对原序列建可持久化(Trie),查询时直接在(Trie)上贪心就行。

实现细节看代码吧。

(code:)

#include<bits/stdc++.h>
#define maxn 3000010
using namespace std;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,tot,ans;
int a[maxn],p[maxn],pre[maxn],nxt[maxn];
int rt[maxn],t[maxn][2],cnt[maxn];
bool cmp(const int &x,const int &y)
{
    return a[x]<a[y];
}
void insert(int x,int k,int &p)
{
    cnt[++tot]=cnt[p],t[tot][0]=t[p][0],t[tot][1]=t[p][1];
    p=tot,cnt[p]++;
    if(k==-1) return;
    insert(x,k-1,t[p][(x>>k)&1]);
}
int query(int ql,int qr,int k,int x)
{
    if(k==-1) return 0;
    int ch=((x>>k)&1)^1;
    if(cnt[t[qr][ch]]-cnt[t[ql][ch]])
        return query(t[ql][ch],t[qr][ch],k-1,x)|(1<<k);
    else return query(t[ql][ch^1],t[qr][ch^1],k-1,x);
}
int main()
{
    read(n);
    for(int i=1;i<=n;++i) pre[i]=i-1,nxt[i]=i+1,p[i]=i;
    for(int i=1;i<=n;++i)
        read(a[i]),rt[i]=rt[i-1],insert(a[i],30,rt[i]);
    sort(p+1,p+n+1,cmp);
    for(int i=1;i<=n;++i)
    {
        int l=pre[p[i]],r=nxt[p[i]];
        nxt[l]=r,pre[r]=l;
        if(l) ans=max(ans,query(rt[pre[l]],rt[r-1],30,a[p[i]]));
        if(r!=n+1) ans=max(ans,query(rt[l],rt[nxt[r]-1],30,a[p[i]]));
    }
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/lhm-/p/12819058.html