【luogu P2839】middle
middle
题目链接:luogu P2839
题目大意
有一个数组,给你左区间和右区间的范围,问你构成的子区间中最大的中位数是多少。
思路
这道题你想想中位数是干嘛的。
那假设中位数是 \(x\),我们把 \(<x\) 的看做是 \(-1\),否则看做是 \(1\),那这些数的这个值的和应该是 \(0/1\)。
那如果小于 \(0\),就是不能达到这个中位数的大小,大于 \(1\) 就是超过了这个中位数的大小。
那我们可以以此来二分一个答案。
那我们看看么看和是多少,毕竟你不能直接扫过去。
那你看,它左右区间其实可以分成三段。中间那一段是一定要选的:\(b+1\sim c-1\),左边选最大后缀(至少选一个):\(a\sim b\),右边选最大前缀(至少选一个):\(c\sim d\)。
那这个其实都可以用线段树来维护。
但是你发现你选择的中位数一旦改变,就要重新建一棵树。
那你考虑中位数先按顺序看,到下一个之后,你这个值的值就由 \(1\) 变成了 \(-1\),也只有这个改变了。
那你会想到主席树,然后就好了。
代码
#include<cstdio>
#include<algorithm>
using namespace std;
struct Tree {
int l, r, x, sum, lmax, rmax;
}tree[20001 << 5];
struct node {
int x, num;
}numm[20001];
int n, last_answer, middle, now_sum;
int q, a, b, c, d, lef, rig;
int root[20001], t_n, answer;
bool cmp(node x, node y) {
return x.x < y.x;
}
int clone(int now) {//主席树
t_n++;
tree[t_n] = tree[now];
return t_n;
}
int build(int now, int l, int r) {
now = ++t_n;
if (l == r) {
tree[now].x = 1;
tree[now].sum = tree[now].x;
tree[now].lmax = tree[now].rmax = tree[now].x;
return now;
}
int mid = (l + r) >> 1;
tree[now].l = build(tree[now].l, l, mid);
tree[now].r = build(tree[now].r, mid + 1, r);
//这里主席树还要多记录几个值:区间和,区间前缀最大值,区间后缀最大值
tree[now].sum = tree[tree[now].l].sum + tree[tree[now].r].sum;
tree[now].lmax = max(tree[tree[now].l].lmax, tree[tree[now].l].sum + tree[tree[now].r].lmax);
tree[now].rmax = max(tree[tree[now].r].rmax, tree[tree[now].r].sum + tree[tree[now].l].rmax);
return now;
}
int change(int now, int l, int r, int loc, int x) {
now = clone(now);
if (l == r) {
tree[now].x = tree[now].sum = x;
tree[now].lmax = tree[now].rmax = tree[now].x;
return now;
}
int mid = (l + r) >> 1;
if (loc <= mid) tree[now].l = change(tree[now].l, l, mid, loc, x);
else tree[now].r = change(tree[now].r, mid + 1, r, loc, x);
//上面说的记录的值的维护
tree[now].sum = tree[tree[now].l].sum + tree[tree[now].r].sum;
tree[now].lmax = max(tree[tree[now].l].lmax, tree[tree[now].l].sum + tree[tree[now].r].lmax);
tree[now].rmax = max(tree[tree[now].r].rmax, tree[tree[now].r].sum + tree[tree[now].l].rmax);
return now;
}
int get_sum(int now, int l, int r, int L, int R) {//区间和
if (l >= L && r <= R) {
return tree[now].sum;
}
int mid = (l + r) >> 1, re = 0;
if (L <= mid) re += get_sum(tree[now].l, l, mid, L, R);
if (R >= mid + 1) re += get_sum(tree[now].r, mid + 1, r, L, R);
return re;
}
int get_lef(int now, int l, int r, int L, int R) {//区间前缀最大值
if (l >= L && r <= R) return tree[now].lmax;
int mid = (l + r) >> 1;
if (mid + 1 <= R && mid >= L) return max(get_lef(tree[now].l, l, mid, L, R), get_sum(tree[now].l, l, mid, L, R) + get_lef(tree[now].r, mid + 1, r, L, R));
else if(mid + 1 <= R) return get_lef(tree[now].r, mid + 1, r, L, R);
else if (mid >= L) return get_lef(tree[now].l, l, mid, L, R);
}
int get_rig(int now, int l, int r, int L, int R) {//区间后缀最大值
if (l >= L && r <= R) return tree[now].rmax;
int mid = (l + r) >> 1;
if (mid + 1 <= R && mid >= L) return max(get_rig(tree[now].r, mid + 1, r, L, R), get_sum(tree[now].r, mid + 1, r, L, R) + get_rig(tree[now].l, l, mid, L, R));
else if(mid + 1 <= R) return get_rig(tree[now].r, mid + 1, r, L, R);
else if (mid >= L) return get_rig(tree[now].l, l, mid, L, R);
}
bool check(int middle) {
now_sum = 0;//分成三段,必须要选,左边选一段后缀,右边选一段前缀
if (b + 1 <= c - 1) now_sum += get_sum(root[middle], 1, n, b + 1, c - 1);
now_sum += get_rig(root[middle], 1, n, a, b);
now_sum += get_lef(root[middle], 1, n, c, d);
return now_sum >= 0;
}
void sort_abcd() {//把数排序(一开始没弄数组,后来发现要排序之后懒得改,就直接这样了)
if (a > b) swap(a, b);
if (a > c) swap(a, c);
if (a > d) swap(a, d);
if (b > c) swap(b, c);
if (b > d) swap(b, d);
if (c > d) swap(c, d);
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &numm[i].x);
numm[i].num = i;
}
sort(numm + 1, numm + n + 1, cmp);
root[1] = build(0, 1, n);//按大小插
for (int i = 2; i <= n; i++)
root[i] = change(root[i - 1], 1, n, numm[i - 1].num, -1);
scanf("%d", &q);
for (int times = 1; times <= q; times++) {
scanf("%d %d %d %d", &a, &b, &c, &d);
a = (a + last_answer) % n;//求出输入
b = (b + last_answer) % n;
c = (c + last_answer) % n;
d = (d + last_answer) % n;
a++;
b++;
c++;
d++;
sort_abcd();
lef = 1;
rig = n;
while (lef <= rig) {//二分
middle = (lef + rig) >> 1;
if (check(middle)) {
answer = middle;
lef = middle + 1;
}
else rig = middle - 1;
}
last_answer = numm[answer].x;
printf("%d\n", last_answer);
}
return 0;
}