[洛谷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 行,每一行有三个整数 $ ext{op},l,r(,) ext{op}$ 为 0 代表升序排序,( ext{op}) 为 1 代表降序排序, l,r 表示排序的区间。

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

输出格式

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

样例输入

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

样例输出

5

说明

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

对于 30% 的数据,(n,mleq 1000)

对于 100% 的数据,(n,mleq 10^5)(1leq qleq n)

解析

最简单的做法是每次都排序,但复杂度上显然不能够通过。

考虑优化一下这个做法。首先发现答案具有单调性,那么就二分一个mid,把大于等于mid的数变为1,小于mid的数变成0。排序时,如果是顺序排序,就可以把所有0放在区间前面,1放在区间后面;逆序同理。这可以用线段树实现。如果最后第q为上是1,说明还可以有更大的mid,否则说明当前的mid过大。

代码

#include <iostream>
#include <cstdio>
#define N 100002
using namespace std;
struct SegmentTree{
	int dat,add;
}t[N*4];
struct event{
	int op,l,r;
}p[N];
int n,m,q,i,a[N],l,r,mid,ans;
int read()
{
	char c=getchar();
	int w=0;
	while(c<'0'||c>'9') c=getchar();
	while(c<='9'&&c>='0'){
		w=w*10+c-'0';
		c=getchar();
	}
	return w;
}
void update(int p)
{
	t[p].dat=t[p*2].dat+t[p*2+1].dat;
}
void spread(int p,int l,int r)
{
	if(t[p].add!=-1){
		int mid=(l+r)/2;
		t[p*2].add=t[p].add;
		t[p*2+1].add=t[p].add;
		t[p*2].dat=t[p].add*(mid-l+1);
		t[p*2+1].dat=t[p].add*(r-mid);
		t[p].add=-1;
	}
}
void build(int p,int l,int r,int x)
{
	t[p].add=-1;
	if(l==r){
		t[p].dat=(a[l]>=x);
		return;
	}
	int mid=(l+r)/2;
	build(p*2,l,mid,x);
	build(p*2+1,mid+1,r,x);
	update(p);
}
int ask(int p,int l,int r,int ql,int qr)
{
    if(ql>qr) return 0;
	if(ql<=l&&r<=qr) return t[p].dat;
	int mid=(l+r)/2,ans=0;
	spread(p,l,r);
	if(ql<=mid) ans+=ask(p*2,l,mid,ql,qr);
	if(qr>mid) ans+=ask(p*2+1,mid+1,r,ql,qr);
	return ans;
}
void change(int p,int l,int r,int ql,int qr,int x)
{
    if(ql>qr) return;
	if(ql<=l&&r<=qr){
		t[p].add=x;
		t[p].dat=(r-l+1)*x;
		return;
	}
	int mid=(l+r)/2;
	spread(p,l,r);
	if(ql<=mid) change(p*2,l,mid,ql,qr,x);
	if(qr>mid) change(p*2+1,mid+1,r,ql,qr,x);
	update(p);
}
bool check(int x)
{
	build(1,1,n,x);
	for(int i=1;i<=m;i++){
		int cnt=ask(1,1,n,p[i].l,p[i].r);
		if(p[i].op==0){
			change(1,1,n,p[i].l,p[i].r-cnt,0);
			change(1,1,n,p[i].r-cnt+1,p[i].r,1);
		}
		else{
			change(1,1,n,p[i].l,p[i].l+cnt-1,1);
			change(1,1,n,p[i].l+cnt,p[i].r,0);
		}
	}
	return ask(1,1,n,q,q);
}
int main()
{
	n=read();m=read();
	for(i=1;i<=n;i++){
		a[i]=read();
		r=max(r,a[i]);
	}
	for(i=1;i<=m;i++) p[i].op=read(),p[i].l=read(),p[i].r=read();
	q=read();
	while(l<=r){
		mid=(l+r)/2;
		if(check(mid)) ans=mid,l=mid+1;
		else r=mid-1;
	}
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/LSlzf/p/12190841.html