P2824 [HEOI2016/TJOI2016] 排序

这题是我在做数据结构时做到的,看了题解,发现这种思想挺重要的,记录一下

题目意思很明确,对于一个长度为n排列A进行m次操作,每次操作对于区间[l,r]进行升序或降序排序,询问在m次操作后位置q的数是多少。

考虑二分答案,将大于等于mid的数记为1,小于mid的数记为0,每次排序就是将一个区间划分为两部分,一部分全为1,另一部分全为0。使用线段树维护即可。

下面是我打的代码


#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100010;
int n, m, q, a[N];
struct tt {
	int op, l, r;
}b[N];

struct SEG {
	int sum[4 * N], col[4 * N], tag[4 * N];
	void push_up(int rt, int ls, int rs) {
		if (col[ls] != col[rs] || col[ls] == -1 || col[rs] == -1) col[rt] = -1;
		else col[rt] = col[ls];
		sum[rt] = sum[ls] + sum[rs]; 
	}
	void push_down(int rt, int l, int r, int ls, int rs) {
		 if (tag[rt] == -1) return ;
		 int mid = (l + r) / 2;
		 col[ls] = tag[rt];
		 sum[ls] = tag[rt] * (mid - l + 1);
		 tag[ls] = tag[rt];
		 col[rs] = tag[rt];
		 sum[rs] = tag[rt] * (r - mid);
		 tag[rs] = tag[rt];
		 tag[rt] = -1;
	}
	void update(int root, int l, int r, int pl, int pr, int val) {
		if (pl > pr) return ;
		if (pl <= l && pr >= r) {
			col[root] = val;
			sum[root] = (r - l + 1) * val;
			tag[root] = val;
			return ;
		}
		push_down(root, l, r, root * 2, root * 2 + 1);
		int mid = (l + r) / 2;
		if (pl <= mid) update(root * 2, l, mid, pl, pr, val);
		if (pr > mid) update(root * 2 + 1, mid + 1, r, pl, pr, val);
		push_up(root, root * 2, root * 2 + 1);
	}
	int query(int root, int l, int r, int pl, int pr) {
		if (pl <= l && pr >= r) return sum[root];
		push_down(root, l, r, root * 2, root * 2 + 1);
		int mid = (l + r) / 2, res = 0;
		if (pl <= mid) res += query(root * 2, l, mid, pl, pr);
		if (pr > mid) res += query(root * 2 + 1, mid + 1, r, pl, pr);
		return res;
	}
}seg;

bool check(int mid) {
	seg.update(1, 1, n, 1, n, 0);
	for (int i = 1; i <= n; i++) {
		if (a[i] >= mid) seg.update(1, 1, n, i, i, 1);
	}
	for (int i = 1; i <= m; i++) {
		if (b[i].op == 0) {
			int cnt = seg.query(1, 1, n, b[i].l, b[i].r);
			seg.update(1, 1, n, b[i].l, b[i].r - cnt, 0);
			seg.update(1, 1, n, b[i].r - cnt + 1, b[i].r, 1);
		}else {
			int cnt = seg.query(1, 1, n, b[i].l, b[i].r);
			seg.update(1, 1, n, b[i].l, b[i].l + cnt - 1, 1);
			seg.update(1, 1, n, b[i].l + cnt, b[i].r, 0);
		}
	}
	return seg.query(1, 1, n, q, q) == 1;
}

signed main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
	for (int i = 1; i <= m; i++) scanf("%lld%lld%lld", &b[i].op, &b[i].l, &b[i].r);
	cin >> q;
	int l = 1, r = n, ans;
	memset(seg.tag, -1, sizeof(seg.tag));
	while (l <= r) {
		int mid = (l + r) / 2;
		if (check(mid)) {
			ans = mid;
			l = mid + 1;
		}else r = mid - 1;
	}
	cout << ans;
}

对于这种思想可以解决诸多问题,将问题和操作简化来实现目的。不用关心每个数具体是多少,而是关心相对大小,将数字进行分组。还有类似的题需要求平均数的也用到了这种思想,二分答案,将每个数减去这个答案,看看有没有区间和大于等于0。

posted @ 2026-08-05 17:19  gw2204  阅读(3)  评论(0)    收藏  举报