[二分] [线段树] P2824 [HEOI2016_TJOI2016] 排序

posted on 2024-06-19 05:14:46 | under | source

离线做法:

考虑到对序列直接排序是 hard 的,但是若 \(a\in \{0,1\}\) 的话不难用线段树直接维护。具体来说,算出区间 \(1\) 的个数,升序排列就是将所有 \(0\) 放在左边,\(1\) 放在右边,降序同理。

怎么转化成这种形式?可以使用二分,令 \(c_i=[a_i\ge mid]\),对 \(01\) 序列 \(c\) 进行操作即可。若最后 \(c_q=1\),则也有 \(a_q\ge mid\)

好像有点玄乎,但是仔细想想,因为是二分答案,所以只需要判断 \(a\)\(mid\) 的偏序关系,若 \(c_x=c_y\) 那么就不需要考虑它们的相对顺序——因为 \(a_x,a_y\) 在此时是“等价”的。于是直接对 \(c\) 序列处理即可。

在线做法:

使用线段树合并、分裂。

将序列视作若干个有序区间,可以用 set 维护,然后对它们每个都单独开一颗权值线段树。

那么对于被操作区间完全覆盖的有序区间,对它们做线段树合并即可。

而于其余的区间(至多两个),对它们做线段树分裂得到被包含的部分,然后它们再合并进大区间即可。

代码

离线。

#include<bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
int n, m, a[N], q, lim;
struct modify{int l, r, opt;} b[N]; 

namespace Sg_Tree{
	#define lt (u << 1)
	#define rt (u << 1 | 1)
	#define mid (l + r >> 1)
	int t[N << 2], tag[N << 2];
	
	inline void psup(int u) {t[u] = t[lt] + t[rt];}
	inline void psdw(int u, int l, int r){
		if(tag[u] != -1) t[lt] = tag[u] * (mid - l + 1), t[rt] = tag[u] * (r - mid), tag[lt] = tag[rt] = tag[u];
		tag[u] = -1;
	}
	inline void build(int u, int l, int r){
		tag[u] = -1;
		if(l == r) {t[u] = {a[l] >= lim}; return ;}
		build(lt, l, mid), build(rt, mid + 1, r);
		psup(u);
	}
	inline void upd(int u, int l, int r, int ll, int rr, int p){
		if(ll > rr) return ;
		if(ll <= l && r <= rr) {t[u] = p * (r - l + 1), tag[u] = p; return ;}
		psdw(u, l, r);
		if(ll <= mid) upd(lt, l, mid, ll, rr, p);
		if(rr > mid) upd(rt, mid + 1, r, ll, rr, p);
		psup(u);
	}
	inline int fid(int u, int l, int r, int ll, int rr){
		if(ll > rr) return 0;
		if(ll <= l && r <= rr) return t[u];
		psdw(u, l, r); int res = 0;
		if(ll <= mid) res += fid(lt, l, mid, ll, rr);
		if(rr > mid) res += fid(rt, mid + 1, r, ll, rr);
		psup(u); return res; 
	}
}using namespace Sg_Tree;
inline bool check(int Mid){
	lim = Mid;
	build(1, 1, n);
	for(int i = 1; i <= m; ++i){
		int cnt = fid(1, 1, n, b[i].l, b[i].r);
		if(!b[i].opt) 
			upd(1, 1, n, b[i].l, b[i].r - cnt, 0), upd(1, 1, n, b[i].r - cnt + 1, b[i].r, 1);
		else
			upd(1, 1, n, b[i].l, b[i].l + cnt - 1, 1), upd(1, 1, n, b[i].l + cnt, b[i].r, 0);
	}
	return fid(1, 1, n, q, q);
}
signed main(){
	cin >> n >> m;
	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	for(int i = 1; i <= m; ++i) scanf("%d%d%d", &b[i].opt, &b[i].l, &b[i].r);
	cin >> q;	
	int L = 1, R = n + 1, Mid;
	while(L + 1 < R){
		Mid = (L + R) >> 1;
		if(check(Mid)) L = Mid;
		else R = Mid;
	}
	cout << L;
	return 0;
}
posted @ 2026-01-12 20:13  Zwi  阅读(1)  评论(0)    收藏  举报