洛谷 P2824 [HEOI2016/TJOI2016]排序

Description

洛谷传送门

Solution

一眼就能想到线段树,但是线段树似乎也无法维护啊?那怎么做?

于是我们查看标签……

发现……二分答案?这怎么二分答案??

于是我们再回想一下题目要求计算什么:位置是 \(q\) 的数是多少。

而且只有一次询问。

既然是二分答案,那我们就二分呗。

假设答案是 \(mid\),那现在的问题就是判断 \(mid\) 是不是操作完之后是第 \(q\) 个数。

考虑到线段树只能进行区间维护,比如区间加,区间修改,区间求和什么的……

诶,等等,如果我们把小于 \(mid\) 的数都重新赋值为 0,大于等于 \(mid\) 的数都赋值为 1,那这个排序是不是就相当于区间赋值了呢?对于从小到大排序,我们不需要知道数到底是多少,只需要把 0 全都放到区间的前半段,1 放到后半段即可。反之同理。

操作完之后,查询第 \(q\) 位是 0 还是 1,如果是 0,说明这个 \(mid\) 太大了,需要调小,反之则需要调大。

这样一来,这道题就可以完美的解决了,时间复杂度 \(O(nlog^2_n)\)

最后再讲一下如何区间修改:我们记录一下区间和,那么这个区间和就是区间里 1 的个数,每次修改时先查询一下区间和,然后覆盖即可,具体看代码吧。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ls rt << 1
#define rs rt << 1 | 1

using namespace std;

inline int read(){
	int x = 0;
	char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return x;
}

const int N = 1e5 + 10;
int n, m, maxs, p, ans;
int a[N], sum[N << 2], lazy[N << 2];
bool b[N];
struct node{
	int op, l, r;
}q[N];

inline void pushup(int rt){
	sum[rt] = sum[ls] + sum[rs];
}

inline void pushdown(int l, int r, int rt){
	if(lazy[rt] != -1){
		int mid = (l + r) >> 1;
		sum[ls] = lazy[rt] * (mid - l + 1);
		sum[rs] = lazy[rt] * (r - mid);
		lazy[ls] = lazy[rs] = lazy[rt];
		lazy[rt] = -1;
	}
}

inline void build(int l, int r, int rt){
	lazy[rt] = -1;
	if(l == r){
		sum[rt] = b[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, ls);
	build(mid + 1, r, rs);
	pushup(rt);
}

inline int query(int L, int R, int l, int r, int rt){
	if(L <= l && r <= R) return sum[rt];
	pushdown(l, r, rt);
	int mid = (l + r) >> 1;
	int res = 0;
	if(L <= mid) res += query(L, R, l, mid, ls);
	if(R > mid) res += query(L, R, mid + 1, r, rs);
	pushup(rt);
	return res;
}

inline void update(int L, int R, int k, int l, int r, int rt){
	if(L <= l && r <= R){
		sum[rt] = k * (r - l + 1);
		lazy[rt] = k;
		return;
	}
	pushdown(l, r, rt);
	int mid = (l + r) >> 1;
	if(L <= mid) update(L, R, k, l, mid, ls);
	if(R > mid) update(L, R, k, mid + 1, r, rs);
	pushup(rt);
}

inline bool check(int mid){
	for(int i = 1; i <= n; i++)
		b[i] = a[i] >= mid;
	build(1, n, 1);
	for(int i = 1; i <= m; i++){
		int s = query(q[i].l, q[i].r, 1, n, 1);
		if(!s || s == q[i].r - q[i].l + 1) continue;
		if(!q[i].op) update(q[i].l, q[i].r - s, 0, 1, n, 1), update(q[i].r - s + 1, q[i].r, 1, 1, n, 1);
		else update(q[i].l, q[i].l + s - 1, 1, 1, n, 1), update(q[i].l + s, q[i].r, 0, 1, n, 1);
	}
	return query(p, p, 1, n, 1);
}

int main(){
	n = read(), m = read();
	for(int i = 1; i <= n; i++)
		a[i] = read();
	for(int i = 1; i <= m; i++)
		q[i].op = read(), q[i].l = read(), q[i].r = read();
	p = read();
	int l = 1, r = n;
	while(l <= r){
		int mid = (l + r) >> 1;
		if(check(mid)) ans = mid, l = mid + 1;
		else r = mid - 1;
	}
	printf("%d\n", ans);
	return 0;
}

End

posted @ 2021-10-14 16:29  xixike  阅读(35)  评论(0编辑  收藏  举报