大致题意:一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a, b从0开始标号,除法取下整。给你一个长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。其中a < b < c < d。位置也从0开始标号。强制在线。
对于中位数有一个性质,将所有大于等于他的数置为
1
, 小于他的数置为
定长的区间我们已经会求解中位数了,现在的问题是,左端点在
会发现若上述”左端点不固定的若干区间” + “定区间” + “右端点不定的若干区间”组成的新区间的最大子段和大于等于
0
,则我们二分出的答案必然是某个区间的中位数。
二分答案,以及如何判定这个答案是否满足每个询问区间的方法我们已经知道了。
现在的问题就是,如何快速得到一个区间的最大子段和,这是一个经典的线段树问题。
其实我们还剩下最后一个问题,每次二分出一个答案,所有数组都在变化,总不能每个询问都建立log个线段树。
其实只要想一下就会发现,这题中出现的数组的种类只有
如果从小到大插入数的话,其实相对上一次插入只会使得一个数变化。因此可以考虑用可持久化线段树来完成动态修改过程。
复杂度
O(nlognlogn)
.
#include <bits/stdc++.h>
#define all(x) x.begin(), x.end()
using namespace std;
const int maxn = 22000;
int n, cid;
int val[maxn];
vector<pair<int, int> > V;
struct Seg { int l, r, sum, lsum, rsum;} tr[maxn*40];
int root[maxn];
int ans = 0;
void pushup(int x) {
tr[x].sum = tr[tr[x].l].sum + tr[tr[x].r].sum;
tr[x].lsum = max(tr[tr[x].l].lsum, tr[tr[x].l].sum + tr[tr[x].r].lsum);
tr[x].rsum = max(tr[tr[x].r].rsum, tr[tr[x].r].sum + tr[tr[x].l].rsum);
}
int build(int l, int r) {
int x = ++ cid;
if(l == r) {
tr[x].sum = tr[x].lsum = tr[x].rsum = 1;
return x;
}
int m = l + r >> 1;
tr[x].l = build(l, m);
tr[x].r = build(m+1, r);
pushup(x);
return x;
}
int update(int y, int l, int r, int pos, int val) {
int x = ++cid;
if(l == r) {
tr[x].sum = tr[x].rsum = tr[x].lsum = val;
return x;
}
int m = l + r >> 1;
if(pos <= m) {
tr[x].r = tr[y].r;
tr[x].l = update(tr[y].l, l, m, pos, val);
} else {
tr[x].l = tr[y].l;
tr[x].r = update(tr[y].r, m+1, r, pos, val);
}
pushup(x);
return x;
}
int ask(int L, int R, int l, int r, int x) {
if(L > R) return 0;
if(L <= l && R >= r){
return tr[x].sum;
}
int m = (l + r) >> 1;
int res = 0;
if(L <= m)
res += ask(L, R, l, m, tr[x].l);
if(R > m)
res += ask(L, R, m+1, r, tr[x].r);
return res;
}
int askl(int L, int R, int l, int r, int x) {
if(L > R) return 0;
if(L <= l && R >= r) return tr[x].lsum;
int m = (l + r) >> 1;
if(R <= m)
return askl(L, R, l, m, tr[x].l);
else if(L > m)
return askl(L, R, m+1, r, tr[x].r);
else
return max(askl(L, R, l, m, tr[x].l), ask(L, R, l, m, tr[x].l) + askl(L, R, m+1, r, tr[x].r));
}
int askr(int L, int R, int l, int r, int x) {
if(L > R) return 0;
if(L <= l && R >= r)
return tr[x].rsum;
int m = (l + r) >> 1;
if(R <= m)
return askr(L, R, l, m, tr[x].l);
else if(L > m)
return askr(L, R, m+1, r, tr[x].r);
else
return max(askr(L, R, m+1, r, tr[x].r), askr(L, R, l, m, tr[x].l) + ask(L, R, m+1, r, tr[x].r));
}
void init() {
cid = 0;
}
bool check(int x, int l1, int r1, int l2, int r2) {
int cnt = 0;
cnt += ask(r1+1, l2-1, 0, n-1, root[x]);
cnt += askr(l1, r1, 0, n-1, root[x]);
cnt += askl(l2, r2, 0, n-1, root[x]);
return cnt >= 0;
}
int main() {
init();
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%d", &val[i]);
V.push_back({val[i], i});
}
sort(all(V));
root[0] = build(0, n-1);
for(int i = 1; i <= n; i++)
root[i] = update(root[i-1], 0, n-1, V[i-1].second, -1);
int q;
scanf("%d", &q);
while(q--) {
vector<int> tp;
for(int i = 1; i <= 4; i++) {
int v;
scanf("%d", &v);
v += ans, v %= n;
tp.push_back(v);
}
sort(all(tp));
int l = 1, r = n + 1, m;
while(l < r) {
m = l + r >> 1;
if(check(m, tp[0], tp[1], tp[2], tp[3])) l = m + 1;
else r = m;
}
printf("%d\n", ans = V[l-1].first);
}
return 0;
}