( ext{Solution:})
考虑一个定区间怎么找中位数。
有一个套路:二分一个答案 (mid) ,将大于等于 (mid) 的数设为 (1,) 其他设为 (-1.) 那中位数一定是让区间内的数和为 (0) 的数。
关于这里的边界,根据题目要求改,这一题的要求是这样:偶数时是二者靠后的一个。
所以这个题可以类比这个思路:二分一个答案,然后给数设为正负一……
然后我们发现,多次二分设置数的复杂度显然爆炸。考虑能不能优化这个过程。
观察 (mid o mid+1) 的变化过程有哪些数会被更改:显然只有 (mid) 出现的地方的数变成了 (-1.)
那么我们可以暴力存下每一个 (mid) 出现的位置并暴力单点修改。单点修改的次数最多 (n) 次。
至于 (mid) 我们可以离散化掉。
那么思路的模型就是:对每一个二分的答案 (mid) 建立一棵以序列位置为下标的线段树并考虑快速修改前一棵树上的信息形成下一棵树。
那这个东西就长得很像主席树了。考虑用主席树维护这个东西即可。
那么,区间不确定应该怎么做?
考虑两个区间必然被选择的部分: ([b+1,c-1]) 这一部分的数一定会被全选。所以需要维护区间的和。
如果要让中位数最大,显然我们需要更多的 (1) 才是更优的。那么,对于区间 ([a,b]) 我们自然想到要维护一个最大后缀和。对于 ([c,d]) 维护最大前缀和。
这样得出的答案一定更为优秀。
那么我们主席树上维护的东西就是:区间和,区间左端连续最大子段和,区间右端连续最大子段和。
查询的时候如果求出的 (sum) 大于等于 (0) 就可以让它变得更大。复杂度两个 (log).
注意查询的细节:可以将得到的 (log n) 个节点都提出来单独 pushup 一下来获得信息即可。(新技能 get )
启示就是:
"启发了在值域上建区间的思路。"
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')w=-1;
ch=getchar();
}
while(isdigit(ch))s=s*10+ch-48,ch=getchar();
return w*s;
}
const int MAXN=5e5+10;
int node,rt[MAXN],n,m,a[MAXN];
struct Tr{int ls,rs,sum,llen,rlen;}tr[MAXN<<5];
inline int Max(int x,int y){return x>y?x:y;}
inline int Min(int x,int y){return x<y?x:y;}
int b[MAXN],blen,lastans=0,q[4];
vector<int>vec[MAXN];
inline int Find(int x){
int L=1,R=blen;
int ans=-1;
while(L<=R){
int mid=(L+R)>>1;
if(b[mid]<=x)ans=mid,L=mid+1;
else R=mid-1;
}
return ans;
}
inline int getpos(int x){return Find(x);}
inline void pushup(int x){
tr[x].sum=tr[tr[x].ls].sum+tr[tr[x].rs].sum;
tr[x].llen=Max(tr[tr[x].ls].llen,tr[tr[x].ls].sum+tr[tr[x].rs].llen);
tr[x].rlen=Max(tr[tr[x].rs].rlen,tr[tr[x].rs].sum+tr[tr[x].ls].rlen);
}
int change(int x,int l,int r,int pos,int v){
int p=++node;
tr[p]=tr[x];
if(l==r){
tr[p].sum=v;
tr[p].llen=tr[p].rlen=v;
return p;
}
int mid=(l+r)>>1;
if(pos<=mid)tr[p].ls=change(tr[p].ls,l,mid,pos,v);
else tr[p].rs=change(tr[p].rs,mid+1,r,pos,v);
pushup(p);return p;
}
int query_sum(int x,int L,int R,int l,int r){
if(L>=l&&R<=r)return tr[x].sum;
int mid=(L+R)>>1,res=0;
if(l<=mid)res=query_sum(tr[x].ls,L,mid,l,r);
if(mid<r)res+=query_sum(tr[x].rs,mid+1,R,l,r);
return res;
}
Tr query_left(int x,int L,int R,int l,int r){
if(L>=l&&R<=r)return tr[x];
int mid=(L+R)>>1;
if(l>mid)return query_left(tr[x].rs,mid+1,R,l,r);
else if(r<=mid)return query_left(tr[x].ls,L,mid,l,r);
else{
Tr ans;
Tr Ls=query_left(tr[x].ls,L,mid,l,r);
Tr Rs=query_left(tr[x].rs,mid+1,R,l,r);
ans.sum=Ls.sum+Rs.sum;
ans.llen=Max(Ls.llen,Ls.sum+Rs.llen);
return ans;
}
}
Tr query_right(int x,int L,int R,int l,int r){
if(L>=l&&R<=r)return tr[x];
int mid=(L+R)>>1;
if(l>mid)return query_right(tr[x].rs,mid+1,R,l,r);
else if(r<=mid)return query_right(tr[x].ls,L,mid,l,r);
else{
Tr ans;
Tr Ls=query_right(tr[x].ls,L,mid,l,r);
Tr Rs=query_right(tr[x].rs,mid+1,R,l,r);
ans.sum=Ls.sum+Rs.sum;
ans.rlen=Max(Rs.rlen,Rs.sum+Ls.rlen);
return ans;
}
}
bool check(int x,int A,int B,int C,int D){
int res1=0,res2=0,res3=0;
if(B+1<=C-1)res1=query_sum(rt[x],1,n,B+1,C-1);
res2=query_right(rt[x],1,n,A,B).rlen;
res3=query_left(rt[x],1,n,C,D).llen;
return (res1+res2+res3)>=0;
}
int build(int L,int R){
int p=++node;
int mid=(L+R)>>1;
if(L==R){
tr[p].sum=tr[p].llen=tr[p].rlen=1;
return p;
}
tr[p].ls=build(L,mid);
tr[p].rs=build(mid+1,R);
pushup(p);return p;
}
int main(){
// freopen("1.in","r",stdin);
// freopen("my.out","w",stdout);
n=read();
for(int i=1;i<=n;++i)a[i]=read(),b[i]=a[i];
sort(b+1,b+n+1);
blen=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;++i){
int pos=getpos(a[i]);
a[i]=pos;
vec[pos].push_back(i);
}
rt[1]=build(1,n);
for(int i=2;i<=n;++i){
rt[i]=rt[i-1];
for(int j=0;j<(int)vec[i-1].size();++j){
int pos=vec[i-1][j];
rt[i]=change(rt[i],1,n,pos,-1);
}
}
m=read();
for(int i=1;i<=m;++i){
int A=read(),B=read(),C=read(),D=read();
q[0]=(A+lastans)%n;
q[1]=(B+lastans)%n;
q[2]=(C+lastans)%n;
q[3]=(D+lastans)%n;
sort(q,q+4);
A=q[0],B=q[1],C=q[2],D=q[3];
A++;B++;C++;D++;
int L=1,R=blen,ans=-1;
while(L<=R){
int mid=(L+R)>>1;
if(check(mid,A,B,C,D))ans=mid,L=mid+1;
else R=mid-1;
}
// printf("[%d %d],[%d %d]
",A,B,C,D);
printf("%d
",lastans=b[ans]);
}
return 0;
}