很妙的一道题。二分答案的检验很妙妙。
如何检验mid比答案大了还是小了?
把大等于mid的数都变成1,小于mid的数都变成-1。在一个区间内,如果一个数是中位数,那么区间和就是0(偶数个数)或者1(奇数个数)。
对于一个区间与二分的答案mid,
如果这个区间的总和等于0,那mid就是中位数了。
如果这个区间的总和大于0,那么在这个区间内一定会存在一个大于mid的数,使得区间总和变小。因为如果mid变大了,那么-1就会增多,1就会减少。——这就说明mid比答案小了。
反之,如果总和小于0,那么一定存在一个小于mid的数使得区间总和变大。
那么对于左端点在[a,b],右端点在[c,d]的这个区间,[b+1,c-1]是必须要取的,只需要看[a,b]的最大右缀和与[c,d]的最大左缀和。
如果rans(a,b)+sum(b+1,c-1)+lans(c,d)≥0,就放大答案,
反之缩小答案。
现在问题就变成了如何维护每个数对应出来的序列(全部变成-1和1)。
用主席树维护。这里的线段树对应的区间代表的是下标。(root[i]表示从小到大排第i个的元素的线段树的根。)
先把所有数从小到大排序。那么root[0]就可以把所有数都赋成1,因为它是第0小的(最小的)。
inline void build(int &root,int l,int r){
root=++tot;
if(l==r){T[root].lans=T[root].rans=T[root].sum=1;return;}
build(ls,l,mid),build(rs,mid+1,r);
pushup(root);
}
这样就把第0个树建好了。
考虑root[i]和root[i-1]之间的关系。root[i]只用在root[i-1]的基础上把第(i-1)小的那个位置改成-1就行了。想一想大小关系就好。
递归的时候pushup一下就改好了。
inline void insert(int &root,int pre,int l,int r,int pos){
T[root=++tot]=T[pre];
if(l==r){T[root].lans=T[root].rans=T[root].sum=-1;return;}
if(pos<=mid) insert(ls,T[pre].lc,l,mid,pos);
else insert(rs,T[pre].rc,mid+1,r,pos);
pushup(root);
}
然后询问的时候合并一下线段树节点就好
struct node{
int lans,rans,sum,lc,rc;
node(){lans=rans=sum=0;}
friend inline node operator+(const node &pl,const node &pr){
node ret;
ret.lans=max(pl.lans,pl.sum+pr.lans);
ret.rans=max(pr.rans,pr.sum+pl.rans);
ret.sum=pl.sum+pr.sum;
return ret;
}
}T[maxn*40];
inline node query(int root,int l,int r,int x,int y){
if(l>=x&&r<=y) return T[root];
if(y<=mid) return query(ls,l,mid,x,y);
if(x> mid) return query(rs,mid+1,r,x,y);
return query(ls,l,mid,x,mid)+query(rs,mid+1,r,mid+1,y);
}
check函数写的时候不要把lans和rans搞混了。
inline bool check(int k,int a,int b,int c,int d,int SUM=0){
if(b+1<=c-1)SUM+=query(root[k],0,n-1,b+1,c-1).sum;
SUM+=query(root[k],0,n-1,a,b).rans;
SUM+=query(root[k],0,n-1,c,d).lans;
return SUM>=0;
}
写主席树的时候想好各个参数的含义!(0,n-1)是原序列下标区间。a[i].id是排序后第i大的元素在原序列中的下标。
全部代码:
#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define ls T[root].lc
#define rs T[root].rc
using namespace std;
const int maxn=2e4+10;
int n,m,q,Q[4],root[maxn],lastans=0,tot=0,P,l,r;
struct point{int val,id;}a[maxn];
struct node{
int lans,rans,sum,lc,rc;
node(){lans=rans=sum=0;}
friend inline node operator+(const node &pl,const node &pr){
node ret;
ret.lans=max(pl.lans,pl.sum+pr.lans);
ret.rans=max(pr.rans,pr.sum+pl.rans);
ret.sum=pl.sum+pr.sum;
return ret;
}
}T[maxn*40];
bool cmp(point x,point y){return x.val<y.val;}
inline int read(){
int x=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x;
}
inline void print(int x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
inline void pushup(int root){
T[root].sum=T[ls].sum+T[rs].sum;
T[root].lans=max(T[ls].lans,T[ls].sum+T[rs].lans);
T[root].rans=max(T[rs].rans,T[rs].sum+T[ls].rans);
}
inline void build(int &root,int l,int r){
root=++tot;
if(l==r){T[root].lans=T[root].rans=T[root].sum=1;return;}
build(ls,l,mid),build(rs,mid+1,r);
pushup(root);
}
inline void insert(int &root,int pre,int l,int r,int pos){
T[root=++tot]=T[pre];
if(l==r){T[root].lans=T[root].rans=T[root].sum=-1;return;}
if(pos<=mid) insert(ls,T[pre].lc,l,mid,pos);
else insert(rs,T[pre].rc,mid+1,r,pos);
pushup(root);
}
inline node query(int root,int l,int r,int x,int y){
if(l>=x&&r<=y) return T[root];
if(y<=mid) return query(ls,l,mid,x,y);
if(x> mid) return query(rs,mid+1,r,x,y);
return query(ls,l,mid,x,mid)+query(rs,mid+1,r,mid+1,y);
}
inline bool check(int k,int a,int b,int c,int d,int SUM=0){
if(b+1<=c-1)SUM+=query(root[k],0,n-1,b+1,c-1).sum;
SUM+=query(root[k],0,n-1,a,b).rans;
SUM+=query(root[k],0,n-1,c,d).lans;
return SUM>=0;
}
int main(){
n=read();
for(int i=0;i<n;++i)a[i].val=read(),a[i].id=i;
sort(a,a+n,cmp),build(root[0],0,n-1);
for(int i=1;i<n;++i) insert(root[i],root[i-1],0,n-1,a[i-1].id);
q=read();
while(q--){
for(int i=0;i<4;++i) Q[i]=(read()+lastans)%n;
sort(Q,Q+4),l=0,r=n-1;
while(l<=r)
if(check(mid,Q[0],Q[1],Q[2],Q[3])) P=mid,l=mid+1;
else r=mid-1;
lastans=a[P].val,print(lastans),putchar('\n');
}
}