bzoj 2653 二分答案+可持久化线段树

  首先离散化,然后我们知道如果对于一个询问的区间[l1,r1],[l2,r2],我们二分到一个答案x,将[l1,r2]区间中的元素大于等于x的设为1,其余的设为-1,那么如果[l1,r1]的最大右区间和加上[r1,l2]的区间和加上[l2,r2]的最大左区间和大于等于0,那么最大的中位数一定大于等于x。因为这个区间中大于等于x的数量超过了一半,那么我们可以二分答案,然后判断最大的合法(见上文)区间和是否大于等于0。

  那么对于每个我们二分的值的区间-1,1情况我们不能建立n颗线段树,我们可以建立可持久化线段树来维护这个,最开始的初始值都为1,设rot[x]为二分的值为x的时候区间的1,-1情况的线段树,可以由rot[x-1]这颗线段树继承过来。

  反思:之前写的可持久化线段树都是建立的权值线段树,这次是用线段树维护区间值的,而且之前对于rot[x]只会有一次插入,这道题的rot[x]可能会有多次插入,在这里纠结了半天。还有我就是最后输出的是adr[ans],但是前面强制在线的时候我加的是ans,忘了改了= =。

/**************************************************************

    Problem: 2653

    User: BLADEVIL

    Language: C++

    Result: Accepted

    Time:2012 ms

    Memory:18196 kb

****************************************************************/

 

//By BLADEVIL

#include <cstdio>

#include <cstring>

#include <algorithm>

#define maxn 50010

 

using namespace std;

 

struct rec {

    int key,ans,num;

    rec() {

        key=ans=num=0;

    }

}a[maxn];

 

struct segment {

    int left,right,maxr,maxl,sum;

    int son[2];

    segment() {

        left=right=maxr=maxl=sum=0;

        memset(son,0,sizeof son);

    }

}t[12*maxn];

 

int n,m,tot;

int rot[maxn],adr[maxn];

 

bool cmp1(rec x,rec y) {

    return x.ans<y.ans;

}

 

bool cmp2(rec x,rec y) {

    return x.num<y.num;

}

 

void update(int x) {

    t[x].sum=t[t[x].son[0]].sum+t[t[x].son[1]].sum;

    t[x].maxl=max(t[t[x].son[0]].sum+t[t[x].son[1]].maxl,t[t[x].son[0]].maxl);

    t[x].maxr=max(t[t[x].son[1]].sum+t[t[x].son[0]].maxr,t[t[x].son[1]].maxr);

}

 

void build(int &x,int l,int r) {

    if (!x) x=++tot;

    t[x].left=l; t[x].right=r;

    if (l==r) {

        t[x].sum=t[x].maxl=t[x].maxr=1;

        return ;

    }

    int mid=t[x].left+t[x].right>>1;

    build(t[x].son[0],l,mid); build(t[x].son[1],mid+1,r);

    update(x);

}

 

void insert(int &x,int rot,int y) {

    if (!x) x=++tot;

    t[x].left=t[rot].left; t[x].right=t[rot].right;

    if (t[x].left==t[x].right) {

        t[x].sum=t[x].maxl=t[x].maxr=-1;

        return ;

    }

    int mid=t[x].left+t[x].right>>1;

    if (y>mid) {

        if (!t[x].son[0]) t[x].son[0]=t[rot].son[0];

        if (t[x].son[1]==t[rot].son[1]) t[x].son[1]=0;

        insert(t[x].son[1],t[rot].son[1],y);

    } else {

        if (!t[x].son[1]) t[x].son[1]=t[rot].son[1];

        if (t[x].son[0]==t[rot].son[0]) t[x].son[0]=0;

        insert(t[x].son[0],t[rot].son[0],y);

    }

    update(x);

}

 

segment combine(segment x,segment y) {

    segment ans;

    ans.sum=x.sum+y.sum;

    ans.maxl=max(x.sum+y.maxl,x.maxl);

    ans.maxr=max(y.sum+x.maxr,y.maxr);

    return ans;

}

 

segment query(int x,int l,int r) {

    if ((t[x].left==l)&&(t[x].right==r)) return t[x];

    int mid=t[x].left+t[x].right>>1;

    if (l>mid) return query(t[x].son[1],l,r); else

    if (r<=mid) return query(t[x].son[0],l,r); else

        return combine(query(t[x].son[0],l,mid),query(t[x].son[1],mid+1,r));

}

 

int main(){

    scanf("%d",&n);

    for (int i=1;i<=n;i++) scanf("%d",&a[a[i].num=i].ans);

    sort(a+1,a+1+n,cmp1);

    int sum=1,cur=a[1].ans; adr[1]=a[1].ans;

    for (int i=1;i<=n;i++) if (a[i].ans==cur) a[i].key=sum; else a[i].key=++sum,adr[sum]=cur=a[i].ans;

    //sort(a+1,a+1+n,cmp2);

    //for (int i=1;i<=n;i++) printf("%d ",a[i].key); printf("\n");

    build(rot[0],1,n);

    for (int i=1;i<=n;i++) insert(rot[a[i].key],rot[a[i].key-1],a[i].num);

    //for (int i=1;i<=tot;i++) printf("%d %d %d %d %d %d %d\n",i,t[i].left,t[i].right,t[i].son[0],t[i].son[1],t[i].maxl,t[i].maxr);

    //for (int i=1;i<=n;i++) printf("%d ",adr[i]); printf("\n");

    scanf("%d",&m);

    int ans=0;

    while (m--) {

        int ask[5]; for (int i=1;i<=4;i++) scanf("%d",&ask[i]);

        for (int i=1;i<=4;i++) ask[i]=(ask[i]+adr[ans])%n+1;

        ans=0;

        sort(ask+1,ask+5);

        int l=1,r=n;

        while (l<=r) {

            int mid=l+r>>1,TOT=0;

            //printf("%d %d %d\n",l,r,a[mid].key);

            //printf("%d %d\n",l,r);

            segment a1=query(rot[a[mid].key-1],ask[1],ask[2]),a2=query(rot[a[mid].key-1],ask[3],ask[4]);

            if (ask[2]+1<=ask[3]-1) TOT=query(rot[a[mid].key-1],ask[2]+1,ask[3]-1).sum; 

            //printf("%d\n",TOT);

            TOT+=a1.maxr+a2.maxl; //printf("%d\n",TOT);

            //printf("%d\n",a1.maxr);

            if (TOT>=0) ans=a[mid].key,l=mid+1; else r=mid-1;

        }

        printf("%d\n",adr[ans]);

    }

    return 0;

}

 

你可能感兴趣的:(线段树)