P2824 [HEOI2016/TJOI2016] 排序

P2824 [HEOI2016/TJOI2016] 排序

题目描述

在 2016 年,佳媛姐姐喜欢上了数字序列。因而她经常研究关于序列的一些奇奇怪怪的问题,现在她在研究一个难题,需要你来帮助她。

这个难题是这样子的:给出一个 \(1\)\(n\) 的排列,现在对这个排列序列进行 \(m\) 次局部排序,排序分为两种:

  • 0 l r 表示将区间 \([l,r]\) 的数字升序排序;
  • 1 l r 表示将区间 \([l,r]\) 的数字降序排序。

注意,这里是对下标在区间 \([l,r]\) 内的数排序。
最后询问第 \(q\) 位置上的数字。

输入格式

输入数据的第一行为两个整数 \(n\)\(m\)\(n\) 表示序列的长度,\(m\) 表示局部排序的次数。

第二行为 \(n\) 个整数,表示 \(1\)\(n\) 的一个排列。

接下来输入 \(m\) 行,每一行有三个整数 \(\text{op},l,r\)\(\text{op}\)\(0\) 代表升序排序,\(\text{op}\)\(1\) 代表降序排序, \(l,r\) 表示排序的区间。

最后输入一个整数 \(q\),表示排序完之后询问的位置。

输出格式

输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第 \(q\) 位置上的数字。

输入输出样例 #1

输入 #1

6 3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3

输出 #1

5

说明/提示

河北省选 2016 第一天第二题。

对于 \(30\%\) 的数据,\(n,m\leq 1000\)

对于 \(100\%\) 的数据,\(n,m\leq 10^5\)\(1\leq q\leq n\)

题意

非常清晰明了。

思路

非常巧妙的判断方法。

首先注意到求的只有一个位置的数,这是思考过程的关键点(也是题目难想的点)。深挖这个线索,可以得到:

  • 我们不需要知道所有数的位置。

  • 可以离线。

我们尝试把所有的精力放在 \(q\) 这一个位置上。比如说我们可以比较某个假设值 \(x\)\(a_q\) 的大小。

同时,我们发现全部排序是昂贵的,同时依次遍历 \(m\) 次操作是必不可少的,因此我们必须要在短时间内处理每次操作的贡献。

综上,尝试二分答案。二分答案 \(x\),看真正的答案是否 \(\ge x\)。套路地,我们将 \(\ge x\) 的数赋值为 \(1\),其余为 \(-1\)。这时,所有 \(\ge x\) 的数等价,\(< x\) 的数等价。那么最终排序后,我们检查位置 \(q\)。如果是 \(1\),就说明 \(a_q\) 属于 \(\ge x\) 这个组里面的。正确性已在上述说明中体现了。

那么只剩最后一个问题了:如何快速处理 \(01\) 串的区间排序?这个简单,其实就是统计区间内 \(1\) 的个数,然后把所有 \(1\) 放后面。这就是一个区间求和,区间改的问题了,使用线段树解决。

这种 “降维思想”,通过降维获取高效率的检查,同时尝试二分等判断方式的方法,可以做到时间 \(\mathcal{O}(n) \to \mathcal{O}(\log ^ 2 n)\)

代码

// Problem: P2824 [HEOI2016/TJOI2016] 排序
// Contest: Luogu
// Memory Limit: 256 MB
// Time Limit: 4000 ms

#include <bits/stdc++.h>
#define int long long
#define Misaka namespace
#define Network std
using Misaka Network;
const int N = 1e5 + 7;

int n, m, a[N], b[N], q, opt[N], L[N], R[N];
struct SGT{
	int val[N << 2], lzy[N << 2];
	#define ls (x << 1)
	#define rs ((x << 1) | 1)
	#define mid ((l + r) >> 1)
	void build(int x, int l, int r){
		lzy[x] = -1;
		if(l == r) return val[x] = b[l], void();
		build(ls, l, mid), build(rs, mid + 1, r);
		val[x] = val[ls] + val[rs];
	}
	void pd(int x, int l, int r){
		if(lzy[x] == -1) return;
		lzy[ls] = lzy[x], lzy[rs] = lzy[x];
		val[ls] = (mid - l + 1) * lzy[x];
		val[rs] = (r - mid) * lzy[x];
		lzy[x] = -1;
	}
	void Mdf(int x, int l, int r, int ql, int qr, int k){
		if(qr < ql) return;
		if(ql <= l && r <= qr){
			val[x] = k * (r - l + 1);
			lzy[x] = k; return;
		}
		pd(x, l, r);
		if(ql <= mid) Mdf(ls, l, mid, ql, qr, k);
		if(mid < qr)  Mdf(rs, mid + 1, r, ql, qr, k);
		val[x] = val[ls] + val[rs];
	}
	int Qur(int x, int l, int r, int ql, int qr){
		if(ql <= l && r <= qr) return val[x];
		pd(x, l, r); int res = 0;
		if(ql <= mid) res += Qur(ls, l, mid, ql, qr);
		if(mid < qr)  res += Qur(rs, mid + 1, r, ql, qr);
		return res;
	}
	void Sort(int opt, int l, int r){
		int res = Qur(1, 1, n, l, r);
		Mdf(1, 1, n, l, r, 0);
		if(opt == 0) Mdf(1, 1, n, r - res + 1, r, 1);
		else Mdf(1, 1, n, l, l + res - 1, 1);
	}
} sgt;

bool check(int x){
	for(int i = 1; i <= n; i ++){
		b[i] = (a[i] >= x ? 1 : 0);
	}
	sgt.build(1, 1, n);
	for(int i = 1; i <= m; i ++){
		sgt.Sort(opt[i], L[i], R[i]);
	}
	return sgt.Qur(1, 1, n, q, q);
}

signed main(){
	ios::sync_with_stdio(0), cin.tie(0);
	
	cin >> n >> m;
	for(int i = 1; i <= n; i ++) cin >> a[i];
	for(int i = 1; i <= m; i ++){
		cin >> opt[i] >> L[i] >> R[i];
	}
	cin >> q;
	
	int l = 0, r = n + 1;
	while(l + 1 < r){
		int Mid = (l + r) >> 1;
		if(check(Mid)) l = Mid;
		else r = Mid;
		
	}
	cout << l << "\n";
	
	return 0;
}
posted @ 2026-07-19 14:07  Trent900  阅读(2)  评论(0)    收藏  举报