题面
解法
其实这道题严格上说并不是主席树,而是可持久化线段树。
- 显然答案满足单调性,假设我们当前二分到的是 m i d mid mid,那么对应的中位数即为 a [ m i d ] a[mid] a[mid]( a [ m i d ] a[mid] a[mid]表示从小到大排完序之后的第 m i d mid mid小,不是这个序列中的数显然可以不用考虑)。然后将小于它的数变成-1,其他数变成1。然后对于 [ a , b ] , [ c , d ] [a,b],[c,d] [a,b],[c,d],中间 [ b , c ] [b,c] [b,c]的部分是一定要选的,其他要选择的部分就是 [ a , b − 1 ] [a,b-1] [a,b−1]的最大后缀和与 [ c + 1 , d ] [c+1,d] [c+1,d]的最大前缀和。如果选择的数的和的最大值非负,那么 m i d mid mid合法。
- 然后就发现这个做法的时间复杂度为
O
(
n
q
log
n
)
O(nq\log n)
O(nqlogn),
可能还会更劣,显然是不行的。 - 那么我们就考虑如何优化这个做法。
- 考虑对于每一个数都建出一棵线段树,比它小的数为-1,不小于它的数为1,,然后维护每一段区间的最大前缀和,最大后缀和与区间和。
- 那么我们每一次在二分的时候只要查询对应的线段树,查询相关信息即可。
- 时间复杂度: O ( q log 2 n ) O(q\log^2n) O(qlog2n),但是空间好像是爆炸了诶……
- 然后我们发现, i i i和 i − 1 i-1 i−1对应的线段树上唯一的不同就是原来 a [ i − 1 ] a[i-1] a[i−1]对应的数的位置在 i i i这棵线段树上从1变成-1了。那么因为只有这一个点发生了改动,所以我们可以直接使用可持久化线段树来维护这个过程就可以了。
- 时间复杂度: O ( q log 2 n ) O(q\log^2n) O(qlog2n),空间复杂度: O ( n log n ) O(n\log n) O(nlogn),足以通过此题。
代码
#include <bits/stdc++.h>
#define PI pair <int, int>
#define mp make_pair
#define inf 1 << 30
#define N 20010
using namespace std;
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int max(int x, int y) {return x > y ? x : y;}
struct Info {
int val, id;
bool operator < (const Info &a) const {return val < a.val;}
} a[N];
struct SegmentTree {
struct Node {int lc, rc, sum, mxl, mxr;} t[N * 35];
int tot;
int mxl(int k) {return t[k].mxl;}
int mxr(int k) {return t[k].mxr;}
int sum(int k) {return t[k].sum;}
void update(int k) {
int lc = t[k].lc, rc = t[k].rc;
t[k].sum = sum(lc) + sum(rc);
t[k].mxl = max(sum(lc) + mxl(rc), mxl(lc));
t[k].mxr = max(sum(rc) + mxr(lc), mxr(rc));
}
void build(int &k, int l, int r) {
k = ++tot;
if (l == r) return t[k] = (Node) {0, 0, 1, 1, 1}, void();
int mid = (l + r) >> 1;
build(t[k].lc, l, mid), build(t[k].rc, mid + 1, r);
update(k);
}
void ins(int &cur, int k, int l, int r, int x) {
cur = ++tot; t[cur] = t[k];
if (l == r) return t[cur].sum = t[cur].mxl = t[cur].mxr = -1, void();
int mid = (l + r) >> 1;
if (x <= mid) ins(t[cur].lc, t[k].lc, l, mid, x);
else ins(t[cur].rc, t[k].rc, mid + 1, r, x);
update(cur);
}
PI queryl(int k, int l, int r, int L, int R) {
if (L > R) return mp(0, 0);
if (L <= l && r <= R) return mp(t[k].sum, t[k].mxl);
int mid = (l + r) >> 1;
if (R <= mid) return queryl(t[k].lc, l, mid, L, R);
if (L > mid) return queryl(t[k].rc, mid + 1, r, L, R);
PI tx = queryl(t[k].lc, l, mid, L, mid), ty = queryl(t[k].rc, mid + 1, r, mid + 1, R), ret;
ret.first = tx.first + ty.first, ret.second = max(tx.first + ty.second, tx.second);
return ret;
}
PI queryr(int k, int l, int r, int L, int R) {
if (L > R) return mp(0, 0);
if (L <= l && r <= R) return mp(t[k].sum, t[k].mxr);
int mid = (l + r) >> 1;
if (R <= mid) return queryr(t[k].lc, l, mid, L, R);
if (L > mid) return queryr(t[k].rc, mid + 1, r, L, R);
PI tx = queryr(t[k].lc, l, mid, L, mid), ty = queryr(t[k].rc, mid + 1, r, mid + 1, R), ret;
ret.first = tx.first + ty.first, ret.second = max(ty.first + tx.second, ty.second);
return ret;
}
} T;
int rt[N];
int main() {
int n; read(n);
for (int i = 1; i <= n; i++) read(a[i].val), a[i].id = i;
sort(a + 1, a + n + 1);
T.build(rt[1], 1, n);
for (int i = 2; i <= n; i++) T.ins(rt[i], rt[i - 1], 1, n, a[i - 1].id);
int q, las = 0; read(q);
while (q--) {
int tx[4];
for (int i = 0; i < 4; i++) read(tx[i]), tx[i] = (tx[i] + las) % n + 1;
sort(tx, tx + 4);
int l1 = tx[0], r1 = tx[1], l2 = tx[2], r2 = tx[3], l = 1, r = n, ans;
while (l <= r) {
int mid = (l + r) >> 1, sum = 0;
PI tx = T.queryr(rt[mid], 1, n, l1, r1 - 1), ty = T.queryl(rt[mid], 1, n, r1, l2), tz = T.queryl(rt[mid], 1, n, l2 + 1, r2);
sum = ty.first + max(0, tx.second) + max(0, tz.second);
if (sum >= 0) ans = mid, l = mid + 1; else r = mid - 1;
}
las = a[ans].val; cout << a[ans].val << "\n";
}
return 0;
}