P2824 [HEOI2016/TJOI2016] 排序

P2824 [HEOI2016/TJOI2016] 排序

[HEOI2016/TJOI2016] 排序

题目描述

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

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

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

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

输入格式

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

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

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

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

输出格式

输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第 q 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 % 30\% 30% 的数据, n , m ≤ 1000 n,m\leq 1000 n,m1000

对于 100 % 100\% 100% 的数据, n , m ≤ 1 0 5 n,m\leq 10^5 n,m105 1 ≤ q ≤ n 1\leq q\leq n 1qn

解析:

排序的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),总的时间复杂度为 O ( n m l o g m ) O(nmlogm) O(nmlogm) ,会超时。

对于 01序列 的排序(以升序为例)可以简化排序的过程:

  • 将前部分置 0,将后部分置 1

01序列 的排序实际上是去区间查询,区间修改:

  • 查询 [ l , r ] [l,r] [l,r] 区间中 1 1 1 的个数为 c n t 1 cnt_1 cnt1
  • 将区间 [ r − c n t 1 + 1 , r ] [r-cnt_1+1, r] [rcnt1+1,r] 置 1,将区间 [ l , r − c n t 1 ] [l, r-cnt_1] [l,rcnt1] 置 0

接下来考虑如何将序列转化为 01序列:

对于猜测答案 x x x,将序列中小于 x x x 的数置 0,大于等于 x x x 的数置 1,即可得到 01序列

答案是否具有单调性:

对 01序列 进行局部排序操作,然后查询 q q q 位置的数:

  • 如果为1,则说明正确答案大于等于 x x x
  • 如果为0,则说明正确答案小于 x x x

因此具有单调性,所以可以二分答案。

总的时间复杂度为 O ( m l o g 2 n ) O(mlog^2n) O(mlog2n)

代码中需要注意的是:

  • 特判一下 全0/全1 的排序
  • 每次二分检查的时候,需要将线段树清空,尤其是 lazytag,不要只清零叶子节点的 lazytag(悲)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 1e5+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

inline int ls(int x){return x << 1;}
inline int rs(int x){return x << 1 | 1;}
struct Query{
	int op, l, r;
}q[maxn];
struct node{
	int sum; // 1 的个数 
	int tag;
}t[maxn << 2];
int n, m, p;
int a[maxn], b[maxn];

void init(int x){
	for(int i = 1; i <= n; i++){
		if(a[i] < x)
			b[i] = 0;
		else
			b[i] = 1;
	}
}
void pushup(int k){
	t[k].sum = t[ls(k)].sum + t[rs(k)].sum;
}
void build(int k, int l, int r){
	t[k].tag = 0;
	if(l == r){
		t[k].sum = b[l];		
		return;
	}
	int mid = (l+r) >> 1;
	build(ls(k), l, mid);
	build(rs(k), mid+1, r);
	pushup(k);
}

// tag = 0 : 无事发生
// tag = 1 : 全为 0
// tag = 2 : 全为 1
void pushdown(int k, int l, int r){
	if(t[k].tag == 0)
		return;
		
	if(t[k].tag == 1){
		t[ls(k)].sum = 0;
		t[rs(k)].sum = 0;
	}
	else if(t[k].tag == 2){
		int mid = (l+r) >> 1;
		t[ls(k)].sum = mid - l + 1;
		t[rs(k)].sum = r - mid;
	}
	t[ls(k)].tag = t[rs(k)].tag = t[k].tag;
	t[k].tag = 0; 
}
void modify(int k, int l, int r, int x, int y, int v){
	if(x <= l && y >= r){
		t[k].tag = v;
		if(v == 1) t[k].sum = 0;
		if(v == 2) t[k].sum = r-l+1;
		return;
	}
	pushdown(k, l, r);
	int mid = (l+r) >> 1;
	if(x <= mid)
		modify(ls(k), l, mid, x, y, v);
	if(y > mid)
		modify(rs(k), mid+1, r, x, y, v);
	pushup(k);
}
int querycnt1(int k, int l, int r, int x, int y){
	if(x <= l && y >= r)
		return t[k].sum;
	int mid = (l+r) >> 1;
	pushdown(k, l, r);
	int res = 0;
	if(x <= mid)
		res += querycnt1(ls(k), l, mid, x, y);
	if(y > mid)
		res += querycnt1(rs(k), mid+1, r, x, y);
	return res;
}
int querypos(int k, int l, int r, int pos){
	if(l == r && l == pos)
		return t[k].sum;
	pushdown(k, l, r);
	int mid = (l+r) >> 1;
	int res;
	if(pos <= mid)
		res = querypos(ls(k), l, mid, pos);
	else
		res = querypos(rs(k), mid+1, r, pos);
	return res;
}
bool check(int x){
	init(x);
	build(1, 1, n);
	for(int i = 1; i <= m; i++){
		int cnt1 = querycnt1(1, 1, n, q[i].l, q[i].r); 
		if(cnt1 == 0 || cnt1 == (q[i].r - q[i].l + 1))
			continue;
		if(q[i].op == 0){ // 升序 
			modify(1, 1, n, q[i].l, q[i].r-cnt1, 1);
			modify(1, 1, n, q[i].r-cnt1+1, q[i].r, 2);
		}
		else if(q[i].op == 1){ // 降序 
			modify(1, 1, n, q[i].l+cnt1, q[i].r, 1);
			modify(1, 1, n, q[i].l, cnt1+q[i].l-1, 2);			 
		}
	}
	int res = querypos(1, 1, n, p);
	return res == 1;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
		cin >> a[i];
	for(int i = 1; i <= m; i++)
		cin >> q[i].op >> q[i].l >> q[i].r;
	cin >> p; 
	int l = 1, r = n, res;
	while(l <= r){
		int mid = (l+r) >> 1;
		if(check(mid)){
			res = mid;
			l = mid + 1;
		}
		else
			r = mid - 1;
	}
	cout << res << endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值