题意:给你一个长度为n的序列s。
回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
强制在线。
思路:首先考虑二分答案,判断可行的方法则是看是否小于他的数可以比大于他的数多。则考虑对于[b+1, c-1]直接求出来,对于[a, b]求最大的小的减大的后缀,[c, d]求最大前缀,可以建线段树维护。对于每一个数建一个线段树,则空间爆了,考虑使用主席树维护
那么对于第i颗主席树,则是小于 i 的都是-1,大于等于 i 的是1,查询的时候二分答案查询在排名 i 的这颗树上查是否能选出序列使得和大于等于0
https://www.luogu.org/problemnew/show/P2839
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define MAXN 20050
using namespace std;
inline int read(){
int f = 1, ret = 0; char ch = getchar();
while(ch < '0' || ch > '9') if(ch == '-') f = -1, ch = getchar();
while(ch >= '0' && ch <= '9') ret = ret*10+int(ch-'0'), ch = getchar();
return ret*f;
}
void print(int x){
if(x < 10) putchar(x+'0');
else{
print(x/10);
putchar(x%10+'0');
}
}
int n, cnt, Q, lastans, q[4], root[MAXN];
struct node1{
int id, num;
bool operator < (const node1 &x) const{
return num < x.num;
}
node1(){}
node1(int _id, int _num){
id = _id, num = _num;
}
}a[MAXN<<2];
struct data{
int lmax, rmax, tot;
};
struct node{
int ls, rs;
data d;
}t[MAXN<<6];
data merge(data a, data b){
data ret;
ret.tot = a.tot + b.tot;
ret.lmax = max(a.lmax, a.tot + b.lmax);
ret.rmax = max(b.rmax, b.tot + a.rmax);
return ret;
}
void build(int &u, int l, int r){
int tmp = u; u = ++cnt; t[u] = t[tmp];
if(l == r) {t[u].d.tot = t[u].d.lmax = t[u].d.rmax = 1; return;}
int mid = (l+r)>>1;
build(t[u].ls, l, mid);
build(t[u].rs, mid+1, r);
t[u].d = merge(t[t[u].ls].d, t[t[u].rs].d);
}
void update(int &u, int l, int r, int x){
int tmp = u; u = ++cnt; t[u] = t[tmp];
if(l == r){t[u].d.tot = t[u].d.lmax = t[u].d.rmax = -1; return;}
int mid = (l+r)>>1;
if(x <= mid) update(t[u].ls, l, mid, x);
else update(t[u].rs, mid+1, r, x);
t[u].d = merge(t[t[u].ls].d, t[t[u].rs].d);
}
data query(int u, int l, int r, int tl, int tr){
if(tl <= l && r <= tr) return t[u].d;
int mid = (l+r)>>1;
if(tr <= mid) return query(t[u].ls, l, mid, tl, tr);
if(mid < tl) return query(t[u].rs, mid+1, r, tl, tr);
else return merge(query(t[u].ls, l, mid, tl, tr), query(t[u].rs, mid+1, r, tl, tr));
}
bool check(int id, int l1, int r1, int l2, int r2){
int ret = 0;
if(r1+1 <= l2-1) ret += query(root[id], 1, n, r1+1, l2-1).tot;
ret += query(root[id], 1, n, l1, r1).rmax;
ret += query(root[id], 1, n, l2, r2).lmax;
//printf("%d %d\n", id, ret);
return ret>=0;
}
/*
输入格式:
第一行序列长度n。
接下来n行按顺序给出a中的数。
接下来一行Q
然后Q行每行a,b,c,d,我们令上个询问的答案是x(如果这是第一个询问则x=0)。
令数组q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。
将q从小到大排序之后,令真正的要询问的a=q[0],b=q[1],c=q[2],d=q[3]。
n<=20000,Q<=25000*/
int main(){
//freopen("1.in", "r", stdin);
//freopen("1.out", "w", stdout);
scanf("%d", &n);
//n = read();
for(int i = 1; i <= n; i++){
scanf("%d", &a[i].num); a[i].id = i;
//a[i] = node1(i, read());
}
sort(a+1, a+n+1);
build(root[1], 1, n);
for(int i = 2; i <= n; i++) root[i] = root[i-1], update(root[i], 1, n, a[i-1].id);
scanf("%d", &Q);//Q = read();
while(Q--){
for(int i = 0; i < 4; i++) scanf("%d", &q[i]), q[i] = (q[i]+lastans)%n;//q[i] = (read()+lastans)%n;
sort(q, q+4); for(int i = 0; i < 4; i++) q[i]++;
int l = 1, r = n, mid, ans;
while(l <= r){
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", lastans = a[ans].num);
//print(lastans = a[ans].num); putchar('\n');
}
return 0;
}