[BZOJ3166][Heoi2013]Alo(可持久化线段树+可持久化tire树)

题目描述

传送门

题解

xor的操作很容易想到可持久化trie树,但是关键是次大值比较头疼。
可以发现,如果可以维护出来每一个点向前和向后第一个以及第二个比它大的值的位置就可以确定区间了。
实际上,可以用可持久化线段树的思想来搞。将所有的值从大到小排序,然后按照每一个点的位置插入线段树,相当于对于每一个点,比它大的点已经都插入进去了。每一次查询就是从权值大于当前点的线段树里找出当前点前驱的前驱,一个后继,或者是找出后继的后继,一个前驱,来确定一个区间,这样就可以用可持久化trie树来搞了。
这道题维护的两个量都牵扯到了可持久化,让我对可持久化有了更全面的认识。

我比较傻逼,据说我用了可持久化线段树的那个东西用个数组随便维护一下就行了T_T
UPD:怎么感觉线段树根本不用可持久化啊…蠢死了

代码

#include
#include
#include
#include
using namespace std;
#define inf 1000000000
#define N 50005
#define lg 30

struct hp{int val,id,rk;}a[N];
int n,ans,Max;

namespace pt
{
    int sz;
    int root[N],maxn[N*35],minn[N*35],ls[N*35],rs[N*35];
    void clear()
    {
        memset(minn,127,sizeof(minn));
    }
    void update(int now)
    {
        maxn[now]=max(maxn[ls[now]],maxn[rs[now]]);
        minn[now]=min(minn[ls[now]],minn[rs[now]]);
    }
    void change(int &now,int l,int r,int x)
    {
        int mid=(l+r)>>1;
        ls[++sz]=ls[now],rs[sz]=rs[now];now=sz;
        if (l==r)
        {
            maxn[now]=minn[now]=x;
            return;
        }
        if (x<=mid) change(ls[now],l,mid,x);
        else change(rs[now],mid+1,r,x);
        update(now);
    }
    int query_max(int now,int l,int r,int lrange,int rrange)
    {
        if (lrange>rrange) return 0;
        int mid=(l+r)>>1,ans=0;
        if (lrange<=l&&r<=rrange) return maxn[now];
        if (lrange<=mid) ans=max(ans,query_max(ls[now],l,mid,lrange,rrange));
        if (mid+1<=rrange) ans=max(ans,query_max(rs[now],mid+1,r,lrange,rrange));
        return ans;
    }
    int query_min(int now,int l,int r,int lrange,int rrange)
    {
        if (lrange>rrange) return n+1;
        int mid=(l+r)>>1,ans=inf+1;
        if (lrange<=l&&r<=rrange) return minn[now];
        if (lrange<=mid) ans=min(ans,query_min(ls[now],l,mid,lrange,rrange));
        if (mid+1<=rrange) ans=min(ans,query_min(rs[now],mid+1,r,lrange,rrange));
        return ans;
    }
}
namespace trie
{
    int sz;
    int root[N],sum[N*35],ch[N*35][2];
    void change(int &now,int x,int dep)
    {
        sum[++sz]=sum[now]+1,ch[sz][0]=ch[now][0],ch[sz][1]=ch[now][1];now=sz;
        if (dep==-1) return;
        int k=(x>>dep)&1;
        change(ch[now][k],x,dep-1);
    }
    void query(int l,int r,int x,int dep)
    {
        if (dep==-1) return;
        int k=(x>>dep)&1;
        if (sum[ch[r][k^1]]-sum[ch[l][k^1]]>0)
        {
            ans|=1<1],ch[r][k^1],x,dep-1);
        }
        else query(ch[l][k],ch[r][k],x,dep-1);
    }
}
int cmpval(hp a,hp b)
{
    return a.val>b.val;
}
int cmpid(hp a,hp b)
{
    return a.idint main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;++i) scanf("%d",&a[i].val),a[i].id=i;
    sort(a+1,a+n+1,cmpval);
    pt::clear();
    for (int i=1;i<=n;++i)
    {
        a[i].rk=i;
        pt::root[i]=pt::root[i-1];
        pt::change(pt::root[i],1,n,a[i].id);
    }
    sort(a+1,a+n+1,cmpid);
    for (int i=1;i<=n;++i)
    {
        trie::root[i]=trie::root[i-1];
        trie::change(trie::root[i],a[i].val,lg);
    }
    for (int i=1;i<=n;++i)
    {
        bool flag;int loc1,loc2;
        flag=false;
        loc1=pt::query_max(pt::root[a[i].rk-1],1,n,1,i-1);
        if (loc1!=0) flag=true;
        loc1=pt::query_max(pt::root[a[i].rk-1],1,n,1,loc1-1);
        loc2=pt::query_min(pt::root[a[i].rk-1],1,n,i+1,n);
        if (loc2>n) loc2=n+1;
        ans=0;
        if (loc11&&flag) trie::query(trie::root[loc1],trie::root[loc2-1],a[i].val,lg);
        Max=max(Max,ans);

        flag=false;
        loc1=pt::query_min(pt::root[a[i].rk-1],1,n,i+1,n);
        if (loc1<=n) flag=true;
        loc1=pt::query_min(pt::root[a[i].rk-1],1,n,loc1+1,n);
        if (loc1>n) loc1=n+1;
        loc2=pt::query_max(pt::root[a[i].rk-1],1,n,1,i-1);
        ans=0;
        if (loc21&&flag) trie::query(trie::root[loc2],trie::root[loc1-1],a[i].val,lg);
        Max=max(Max,ans);
    }
    printf("%d\n",Max);
}

总结

①维护当前点向前第二个大于/小于它的值可以尝试用可持久化数据结构。

你可能感兴趣的:(题解,线段树,省选,可持久化,trie)