【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;
}
posted @ 2021-02-10 14:05  あおいSakura  阅读(91)  评论(0编辑  收藏  举报