P2839 [国家集训队] middle 题解

P2839 [国家集训队] middle 题解

做过这道题之后你能加深对主席树的理解。

首先看到要维护中位数,常见的套路是二分这个中位数并赋 ±1 的值。具体地,我们二分这个中位数 x,把 x 的设为 1,把 <x 的设为 1,对于区间 [l,r] 求和,如果这个和 0,这个区间就是合法的。现在的关键是如何快速维护每一个中位数 x 下的区间 [l,r] 之和。

看到区间求和,朴素的想法是开 n 棵线段树维护。对于题目中的限制,显然 [b,c] 区间必选,[a,b1],[c+1,d] 区间分别需要选后缀最大值和前缀最大值。然后用线段树暴力艹,但这样时空都不太能接受。留意到中位数 x 变更到中位数 x+1 的过程有且只有值为 x 的权值发生了变化,这样一来变化的总个数事实上是 n,于是不妨直接开主席树去维护。

需要留意的有两个细节:

  1. 如果二分到的值不在区间内怎么办?容易发现的是如果当前值 x 合法, 那么区间内第一个 x 的数为中位数是所有的权值没有任何变化,因此同样必定合法。
  2. 离散化后尽量不要去重。要去重也可以,但需要留意的是主席树的值域仍然是 n,否则当若干个点的值相同时会少计算贡献。

代码:

#include <bits/stdc++.h>
#define N 20005
using namespace std;
int n, T;
int A[N], B[N];

int rt[N], tot;
struct Node {
	int lc, rc;
	int lmx, rmx, sm;
	Node() {
		lc = rc = lmx = rmx = sm = 0;
	}
} e[N * 100];
#define lc(i) e[i].lc
#define rc(i) e[i].rc
#define lmx(i) e[i].lmx
#define rmx(i) e[i].rmx
#define sm(i) e[i].sm
void push_up(Node &p, Node ls, Node rs) {
	p.sm = ls.sm + rs.sm;
	p.lmx = max(ls.lmx, ls.sm + rs.lmx);
	p.rmx = max(rs.rmx, rs.sm + ls.rmx);
}
int build(int l, int r) {
	int p = ++tot;
	lmx(p) = rmx(p) = sm(p) = r - l + 1;
	if (l == r) return p;
	int mid = (l + r) >> 1;
	lc(p) = build(l, mid);
	rc(p) = build(mid + 1, r);
	return p;
}
int update(int p, int l, int r, int x, int vl) {
	int q = ++tot;
	e[q] = e[p];
	if (l == r) {
		if (vl == 0) return q;
		lmx(q) = rmx(q) = max(vl, 0);
		sm(q) = vl;
		return q;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) lc(q) = update(lc(q), l, mid, x, vl);
	else rc(q) = update(rc(q), mid + 1, r, x, vl);
	push_up(e[q], e[lc(q)], e[rc(q)]);
	return q;
}
Node query(int p, int l, int r, int ql, int qr) {
	if (l > r || ql > qr || l > qr || ql > r) return Node();
	if (ql <= l && r <= qr) return e[p];
	int mid = (l + r) >> 1;
	Node ans, ls = query(lc(p), l, mid, ql, qr), rs = query(rc(p), mid + 1, r, ql, qr);
	push_up(ans, ls, rs);
	return ans;
}
vector<int>v[N];
int res;

bool chk(int x, int a, int b, int c, int d) {
	Node ls = query(rt[x], 1, n, a, b - 1), ms = query(rt[x], 1, n, b, c), rs = query(rt[x], 1, n, c + 1, d);
	return ls.rmx + ms.sm + rs.lmx >= 0;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> A[i], B[i] = A[i];
	sort(B + 1, B + n + 1);
	rt[0] = build(1, n);
	for (int i = 1; i <= n; i++) v[lower_bound(B + 1, B + 1 + n, A[i]) - B].push_back(i);
	for (int i = 1; i <= n; i++) {
		int fg = 0;
		for (auto j : v[i - 1]) {
			if (!fg) rt[i] = update(rt[i - 1], 1, n, j, -1), fg = 1;
			else rt[i] = update(rt[i], 1, n, j, -1);
		}
		if (!fg) rt[i] = rt[i - 1];
	}
	cin >> T;
	while (T--) {
		vector<int>fk;
		int a, b, c, d;
		cin >> a >> b >> c >> d;
		a = (a + res) % n, b = (b + res) % n, c = (c + res) % n, d = (d + res) % n;
		fk.push_back(a), fk.push_back(b), fk.push_back(c), fk.push_back(d);
		sort(fk.begin(), fk.end());
		a = fk[0], b = fk[1], c = fk[2], d = fk[3];
		++a, ++b, ++c, ++d;
		int l = 1, r = n, mid = 0, ans = 0;
		while (l <= r) {
			mid = (l + r) >> 1;
			if (chk(mid, a, b, c, d)) ans = mid, l = mid + 1;
			else r = mid - 1;
		}
		res = B[ans];
		cout << res << "\n";
	}
	return 0;
}
posted @   长安19路  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示