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

分析

二分出答案,设为d,同时对原序列a进行修改: a i ≥ d , 使 b i = 1 , 其他为 0 a_i \ge d,使b_i = 1,其他为0 aid,使bi=1,其他为0 几个问题:

  1. 01序列排序?对01序列排序可以利用线段树的区间覆盖,如对于一个01序列,左端点l,右端点r,共有num个1,则覆盖 [ l , l + n u m − 1 ] = 1 , [ l + n u m , r ] = 0 [l,l+num-1]=1,[l+num,r]=0 [l,l+num1]=1,[l+num,r]=0完成降序排序
  2. 判断是否合法?对于二分的d,随d的增加,b序列0的个数增大(其实是不降),符合二分单调性,当 b q = 1 b_q=1 bq=1时,可以增大d求解, b q = 0 b_q=0 bq=0,反之;
#include 
using namespace std;
const int M = 1e7+10;
#define int long long
struct node{
	int op,l,r;
}qs[M];
istream& operator >> (istream& in,node& t){
	in>>t.op>>t.l>>t.r;
	return in;
}
int a[M];
int n,m,q;int maxn=0;
struct segment {
#define lc(p) (p<<1)
#define rc(p) ((p<<1)|1)
	int tag[M << 2], sum[M << 2];
	bool vis[M<<2];
	void f(int o, int l, int r, int k) {
		tag[o] = k;vis[o]=1;
		sum[o] = k * (r - l + 1);
	}
	void push_up(int x) {
		sum[x] = sum[lc(x)] + sum[rc(x)];
	}
	void push_down(int o, int l, int r) {
		if (!vis[o]) return;
		int mid = l + r >> 1;
		f(lc(o), l, mid, tag[o]);
		f(rc(o), mid + 1, r, tag[o]);
		tag[o] = 0;vis[o]=0;
	}
	void build(int o, int l, int r,int k) {
		vis[o]=0;
		if (l == r) { sum[o] = a[l]>=k; return; }
		int mid = l + r >> 1;
		build(lc(o), l, mid,k);
		build(rc(o), mid + 1, r,k);
		push_up(o);
	}
	int query(int ql, int qr, int o, int l, int r) {
		if(ql>r or qr<l)return 0;
		if (ql <= l and r <= qr) return sum[o];
		int ans = 0;
		int mid = l + r >> 1;
		push_down(o, l, r);
		if (ql<=mid) ans += query(ql, qr, lc(o), l, mid);
		if (mid+1<=qr) ans+=query(ql,qr,rc(o),mid+1,r);
		push_up(o);
		return ans;
	}
	void update(int ql,int qr,int o,int l,int r,int k){
		if(ql>r or qr<l)return;
		if(ql<=l and r<=qr) {f(o,l,r,k);return;}
		push_down(o,l,r);
		int mid = l + r >> 1;
		if (ql<=mid) update(ql, qr, lc(o), l, mid,k);
		if (mid+1<=qr) update(ql,qr,rc(o),mid+1,r,k);
		push_up(o);
	}
}t1;
void read(){
	cin>>n>>m;
	for (int i=1;i<=n;i++) cin>>a[i],maxn=max(maxn,a[i]);
	for (int i=1;i<=m;i++) cin>>qs[i];
	cin>>q;
}
bool check(int d){
	t1.build(1,1,n,d);
	int num;
	for (int i=1;i<=m;i++){
		num=t1.query(qs[i].l,qs[i].r,1,1,n);
		if(qs[i].op) t1.update(qs[i].l,qs[i].l+num-1,1,1,n,1),t1.update(qs[i].l+num,qs[i].r,1,1,n,0);
		else {
			num=qs[i].r-qs[i].l+1-num;
			t1.update(qs[i].l,qs[i].l+num-1,1,1,n,0),t1.update(qs[i].l+num,qs[i].r,1,1,n,1);
		}
	}
	return t1.query(q,q,1,1,n);
}
void solve(){
	int l=1,r=maxn,ans;
	while(l<=r){
		int mid=(l+r)>>1;
		if (check(mid)) l=mid+1,ans=mid;
		else r=mid-1;
	}
	cout<<ans;
}
signed main() {
	read();
	solve();
	return 0;
}

分析

只说check函数

bool check(int d){
	t1.build(1,1,n,d);
	int num;
	for (int i=1;i<=m;i++){
		num=t1.query(qs[i].l,qs[i].r,1,1,n);
		if(qs[i].op) t1.update(qs[i].l,qs[i].l+num-1,1,1,n,1),t1.update(qs[i].l+num,qs[i].r,1,1,n,0);
		else {
			num=qs[i].r-qs[i].l+1-num;
			t1.update(qs[i].l,qs[i].l+num-1,1,1,n,0),t1.update(qs[i].l+num,qs[i].r,1,1,n,1);
		}
	}
	return t1.query(q,q,1,1,n);
}

对于操作1,2,只考虑1,0的个数即可
[ l , l + n u m − 1 ] = 1 , [ l + n u m , r ] = 0 [l,l+num-1]=1,[l+num,r]=0 [l,l+num1]=1,[l+num,r]=0 — —降序排序
[ l , l + n u m − 1 ] = 0 , [ l + n u m , r ] = 1 [l,l+num-1]=0,[l+num,r]=1 [l,l+num1]=0,[l+num,r]=1 — —升序排序

你可能感兴趣的:(算法,c++,数据结构,KISS)