bzoj4552[HEOI2016/TJOI2016]排序(二分+线段树)

题目链接

BZOJ

洛谷

解析

直接对一个序列排序复杂度太高,但对一个\(01\)序列排序就很简单

假设我们认定答案就是\(x\),那么可以将\(1\)\(n\)\(n\)个数分成\(3\)部分:

\[(1) a[i] < x \\ (2) a[i] = x \\ (3) a[i] > x \]

排序时我们只关心三部分之间的大小关系,完全可以不管\((1)\)内部和\((3)\)内部怎么排

再进一步合并\((1)\)\((2)\),那么如果排序后\(q\)位置的数在\((1) \cup (2)\)中,那么\(x\)就可能是答案,如果\(q\)位置的数在\((3)\)中,显然\(x\)不是答案,答案一定大于\(x\)

于是可以二分这个\(x\),然后验证即可

验证的方法是将小于等于\(x\)的数看作\(0\),大于\(x\)的数看作\(1\),用线段树维护区间内\(0\)的个数,排序一个区间就先查询区间内\(0\)的个数\(cnt\),然后把\(l\)\(l + cnt - 1\)修改为\(0\)\(l + cnt\)\(r\)修改为\(1\),最后查询\(q\)位置是否为\(0\)即可

时间复杂度\(O(n \log^2 n)\)

代码

PS.以下代码自带大常数,洛谷5000+ms卡着过

#include <cstdio>
#include <cstring>
#include <iostream>
#define MAXN 100005

typedef long long LL;
struct SegmentTree {
	int cnt0[MAXN << 2], cover[MAXN << 2];
	void pushDown(int, int, int);
	void build(int, int, int);
	int query(int, int, int, int, int);
	void update(int, int, int, int, int, int);
} tree;
int N, M, Q, op[MAXN], left[MAXN], right[MAXN];
int a[MAXN], b[MAXN];

bool check(int);
int main() {
	std::ios::sync_with_stdio(false);
	std::cin >> N >> M;
	for (int i = 1; i <= N; ++i)
		std::cin >> a[i];
	for (int i = 1; i <= M; ++i)
		std::cin >> op[i] >> left[i] >> right[i];
	std::cin >> Q;
	int l = 1, r = N;
	while (l < r) {
		int mid = (l + r) >> 1;
		if (check(mid)) r = mid;
		else l = mid + 1;
	}
	std::cout << l << std::endl;
	
	return 0;
}
void SegmentTree::pushDown(int id, int L, int R) {
	if (cover[id] == -1) return;
	int mid = (L + R) >> 1;
	if (cover[id]) cnt0[id << 1] = cnt0[id << 1 | 1] = 0;
	else cnt0[id << 1] = mid - L + 1, cnt0[id << 1 | 1] = R - mid;
	cover[id << 1] = cover[id << 1 | 1] = cover[id];
	cover[id] = -1;
}
void SegmentTree::build(int id, int L, int R) {
	cover[id] = -1;
	if (L == R) cnt0[id] = (b[L] == 0);
	else {
		int mid = (L + R) >> 1;
		build(id << 1, L, mid);
		build(id << 1 | 1, mid + 1, R);
		cnt0[id] = cnt0[id << 1] + cnt0[id << 1 | 1];
	}
}
void SegmentTree::update(int id, int L, int R, int l, int r, int tp) {
	if (l > r) return;
	if (L >= l && R <= r) {
		cover[id] = tp;
		cnt0[id] = (tp ? 0 : R - L + 1);
	} else {
		pushDown(id, L, R);
		int mid = (L + R) >> 1;
		if (l <= mid) update(id << 1, L, mid, l, r, tp);
		if (r > mid) update(id << 1 | 1, mid + 1, R, l, r, tp);
		cnt0[id] = cnt0[id << 1] + cnt0[id << 1 | 1];
	}
}
int SegmentTree::query(int id, int L, int R, int l, int r) {
	if (L >= l && R <= r) return cnt0[id];
	pushDown(id, L, R);
	int mid = (L + R) >> 1, res = 0;
	if (l <= mid) res += query(id << 1, L, mid, l, r);
	if (r > mid) res += query(id << 1 | 1, mid + 1, R, l, r);
	return res;
}
bool check(int m) {
	for (int i = 1; i <= N; ++i)
		b[i] = (a[i] > m);
	tree.build(1, 1, N);
	for (int i = 1; i <= M; ++i) {
		int t = tree.query(1, 1, N, left[i], right[i]);
		if (op[i]) tree.update(1, 1, N, right[i] - t + 1, right[i], 0), tree.update(1, 1, N, left[i], right[i] - t, 1);
		else tree.update(1, 1, N, left[i], left[i] + t - 1, 0), tree.update(1, 1, N, left[i] + t, right[i], 1);
	}
	return tree.query(1, 1, N, Q, Q);
}
posted @ 2019-02-24 21:48  Rhein_E  阅读(80)  评论(0编辑  收藏  举报