给出一个序列A,长度为n。给出m次询问,每次询问a,b,c,d(a< b< c< d),求a<=l<=b,c<=r<=d的子序列中,最大的中位数是多少。(这里说的中位数就是中间的那个数)强制在线。
n<=20000,m<=25000
不知道有什么数据结构能维护中位数。
考虑二分答案,答案x合法就是存在一个序列,使得大于等于x的数的个数大于小于x的数的个数。
那我们可以吧每一个大于等于x的数看做1,小于x的数看做-1。那么一个序列合法就是它的和>=0。
那我们就相当于求一个左端点在[a~b],右端点在[c~d]的最大子段和。
如果这东西>=0,那么答案合法。
这个东西可以拆分成[a~b]的最大后缀和,[b+1~c-1]的和,和[c~d]最大前缀和。
线段树维护。
那么怎么在时限内建树呢?
发现每一个相邻的x的变化只有一条链,于是可以打可持久化线段树。(动态开节点)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 20005
using namespace std;
struct note{int v,w;}a[N];
struct node{int s,lv,rv,l,r;}t[N*30];
bool cmp(note x,note y) {return x.v<y.v;}
int ans,tot,n,m,x,root[N],q[4];
void back(int v) {
int x=t[v].l,y=t[v].r;
t[v].s=t[x].s+t[y].s;
t[v].lv=max(t[x].lv,t[x].s+t[y].lv);
t[v].rv=max(t[y].rv,t[y].s+t[x].rv);
}
node merge(node y,node z) {
node x;
x.s=y.s+z.s;
x.lv=max(y.lv,y.s+z.lv);
x.rv=max(z.rv,z.s+y.rv);
return x;
}
void build(int &v,int l,int r) {
v=++tot;
if (l==r) {t[v].s=t[v].lv=t[v].rv=1;return;}
int m=(l+r)/2;
build(t[v].l,l,m);build(t[v].r,m+1,r);
back(v);
}
void change(int &v,int l,int r,int x) {
t[++tot]=t[v];v=tot;
if (l==r) {t[v].s=t[v].lv=t[v].rv=-1;return;}
int m=(l+r)/2;
if (x<=m) change(t[v].l,l,m,x);
else change(t[v].r,m+1,r,x);
back(v);
}
node find(int v,int l,int r,int x,int y) {
if (x>y) return t[0];
if (l==x&&r==y) return t[v];
int m=(l+r)/2;
if (y<=m) return find(t[v].l,l,m,x,y);
else if (x>m) return find(t[v].r,m+1,r,x,y);
else return merge(find(t[v].l,l,m,x,m),find(t[v].r,m+1,r,m+1,y));
}
bool check(int x) {
return find(root[x],1,n,q[0],q[1]).rv+find(root[x],1,n,q[1]+1,q[2]-1).s+
find(root[x],1,n,q[2],q[3]).lv>=0;
}
int main() {
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i].v),a[i].w=i;
sort(a+1,a+n+1,cmp);
build(root[1],1,n);
fo(i,2,n) root[i]=root[i-1],change(root[i],1,n,a[i-1].w);
for(scanf("%d",&m);m;m--) {
fo(i,0,3) scanf("%d",&x),q[i]=(ans+x)%n+1;sort(q,q+4);
int l=1,r=n+1,mid;
while (l<r) {
mid=(l+r)/2;
if (check(mid)) l=mid+1;else r=mid;
}
printf("%d\n",ans=a[l-1].v);
}
}