BZOJ 2653: middle|主席树

这题思路确实比较难想!建主席树也是非常的特别!
考虑二分答案,如果当前check的答案是 x 那么大于等于 x 的数的贡献是1,其余的贡献是-1,然后就判断一下是否存在总贡献>= 0 的区间,判断的话需要对所有的数建立一个线段树,然后直接建会MLE+TLE,然后就需要我们的黑科技可持久化的数据结构主席树,先把所有的位置都设为1,然后把数排序后从小到大建树每次-1。
非常神的建树方式,从小到大插入数,插入的权值是这个数的位置!!!
正好和我们的正常思路反着…

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<iostream>
#include<algorithm>
#define ll unsigned long long
#define N 20022
#define mx 1e9
using namespace std;
int sc()
{
    int i=0,f=1; char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
    return i*f;
}
struct W{int v,pos;}h[N];
int sum[N*500],lmx[N*500],rmx[N*500],ch[N*500][2];
int root[N],q[4];
int n,m,cnt,ans;
bool cmp(W a,W b){return a.v<b.v;}
void push_up(int x)
{
    sum[x]=sum[ch[x][0]]+sum[ch[x][1]];
    lmx[x]=max(lmx[ch[x][0]],sum[ch[x][0]]+lmx[ch[x][1]]);
    rmx[x]=max(rmx[ch[x][1]],sum[ch[x][1]]+rmx[ch[x][0]]);
}
void build(int &x,int l,int r)
{
    if(!x)x=++cnt;
    if(l==r)
    {
        sum[x]=lmx[x]=rmx[x]=1;
        return;
    }
    int mid=l+r>>1;
    build(ch[x][0],l,mid);
    build(ch[x][1],mid+1,r);
    push_up(x);
}
void add(int pre,int &x,int l,int r,int v,int f)
{
    if(!x)x=++cnt;
    if(l==r)
    {
        lmx[x]=rmx[x]=sum[x]=f;
        return;
    }
    int mid=l+r>>1;
    if(v<=mid)
        ch[x][1]=ch[pre][1],add(ch[pre][0],ch[x][0],l,mid,v,f);
    else
        ch[x][0]=ch[pre][0],add(ch[pre][1],ch[x][1],mid+1,r,v,f);
    push_up(x);
}
int ask_all(int x,int L,int R,int l,int r)
{
    if(L==l&&R==r)return sum[x];
    int mid=L+R>>1;
    if(r<=mid)return ask_all(ch[x][0],L,mid,l,r);
    else if(l>mid)return ask_all(ch[x][1],mid+1,R,l,r);
    return ask_all(ch[x][0],L,mid,l,mid)+ask_all(ch[x][1],mid+1,R,mid+1,r);
}
int ask_left(int x,int L,int R,int l,int r)
{
    if(L==l&&R==r)return lmx[x];
    int mid=L+R>>1;
    if(r<=mid)return ask_left(ch[x][0],L,mid,l,r);
    else if(l>mid)return ask_left(ch[x][1],mid+1,R,l,r);
    return max(ask_left(ch[x][0],L,mid,l,mid),ask_all(ch[x][0],L,mid,l,mid)+ask_left(ch[x][1],mid+1,R,mid+1,r));
}
int ask_right(int x,int L,int R,int l,int r)
{
    if(L==l&&R==r)return rmx[x];
    int mid=L+R>>1;
    if(r<=mid)return ask_right(ch[x][0],L,mid,l,r);
    else if(l>mid)return ask_right(ch[x][1],mid+1,R,l,r);
    return max(ask_right(ch[x][1],mid+1,R,mid+1,r),ask_all(ch[x][1],mid+1,R,mid+1,r)+ask_right(ch[x][0],L,mid,l,mid));
}
bool check(int x,int a,int b,int c,int d)
{
    int sum=ask_left(root[x],1,n,c,d)+ask_right(root[x],1,n,a,b);
    if(b+1<c) sum+=ask_all(root[x],1,n,b+1,c-1);
    return sum>=0;
}
int main()
{
    n=sc();
    for(int i=1;i<=n;i++)
        h[i].v=sc(),
        h[i].pos=i;
    sort(h+1,h+n+1,cmp);
    build(root[1],1,n);
    for(int i=2;i<=n;i++)
        add(root[i-1],root[i],1,n,h[i-1].pos,-1);
    m=sc();
    for(int i=1;i<=m;i++)
    {
        for(int j=0;j<4;j++)
            q[j]=(sc()+ans)%n+1;
        sort(q,q+4);
        int l=1,r=n;ans=1;
        while(l<=r)
        {
            int mid=l+r>>1;
            if(check(mid,q[0],q[1],q[2],q[3]))ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",ans=h[ans].v);
    }
    return 0;
}

你可能感兴趣的:(主席树)