BZOJ4552: [Tjoi2016&Heoi2016]排序

BZOJ4552: [Tjoi2016&Heoi2016]排序

Description

在2016年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题
,需要你来帮助他。这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排
序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q
位置上的数字。

Input

输入数据的第一行为两个整数n和m。n表示序列的长度,m表示局部排序的次数。1 <= n, m <= 10^5第二行为n个整
数,表示1到n的一个全排列。接下来输入m行,每一行有三个整数op, l, r, op为0代表升序排序,op为1代表降序
排序, l, r 表示排序的区间。最后输入一个整数q,q表示排序完之后询问的位置, 1 <= q <= n。1 <= n <= 10^5,1 <= m <= 10^5

Output

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

Sample Input

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

Sample Output

5

题解Here!

首先,这道题只有一组查询,所以可以二分这个数的排名。

每次二分一个要查询的数在序列中的大小排名(排名指在升序的情况下的排名)。

然后我们原数列进行一些变形:

当$val[i] >= mid$时,此位置就为$1$,反之则为$0$。

这样一来,这道题就变成了一个$01$序列排序,所以就可以用线段树实现$\log_2n$排序。

线段树维护区间和,需要实现区间覆盖。

每次排序前先查询排序一共有多少$1$。

升序排序则将$\left[r-\text{区间1的数量}+1,r\right]$改为$1$,$\left[l,r-\text{区间1的数量}\right]$改为$0$

最后再查询要询问的位置。

若要查询的位置为$1$,那么就增加其排名,即$r=mid-1$

反之,就降低这个数排名。

由于这个数列是$\left[1,n\right]$的全排列,所以二分出的结果就是答案。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define LSON rt<<1
#define RSON rt<<1|1
#define DATA(x) a[x].data
#define SIGN(x) a[x].c
#define LSIDE(x) a[x].l
#define RSIDE(x) a[x].r
#define WIDTH(x) (RSIDE(x)-LSIDE(x)+1)
#define MAXN 100010
using namespace std;
int n,m,q;
int val[MAXN],num[MAXN];
struct Segment_Tree{
	int data,c;
	int l,r;
}a[MAXN<<2];
struct Question{
	int f,l,r;
}que[MAXN];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline void pushup(int rt){
	DATA(rt)=DATA(LSON)+DATA(RSON);
}
inline void pushdown(int rt){
	if(SIGN(rt)==-1||LSIDE(rt)==RSIDE(rt))return;
	SIGN(LSON)=SIGN(rt);
	DATA(LSON)=SIGN(rt)*WIDTH(LSON);
	SIGN(RSON)=SIGN(rt);
	DATA(RSON)=SIGN(rt)*WIDTH(RSON);
	SIGN(rt)=-1;
}
void buildtree(int l,int r,int rt){
	LSIDE(rt)=l;RSIDE(rt)=r;SIGN(rt)=-1;
	if(l==r){
		DATA(rt)=num[l];
		return;
	}
	int mid=l+r>>1;
	buildtree(l,mid,LSON);
	buildtree(mid+1,r,RSON);
	pushup(rt);
}
void update(int l,int r,int c,int rt){
	if(l>r)return;
	if(l<=LSIDE(rt)&&RSIDE(rt)<=r){
		SIGN(rt)=c;
		DATA(rt)=c*WIDTH(rt);
		return;
	}
	pushdown(rt);
	int mid=LSIDE(rt)+RSIDE(rt)>>1;
	if(l<=mid)update(l,r,c,LSON);
	if(mid<r)update(l,r,c,RSON);
	pushup(rt);
}
int query(int l,int r,int rt){
	if(l>r)return 0;
	int ans=0;
	if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt);
	pushdown(rt);
	int mid=LSIDE(rt)+RSIDE(rt)>>1;
	if(l<=mid)ans+=query(l,r,LSON);
	if(mid<r)ans+=query(l,r,RSON);
	return ans;
}
bool check(int mid){
	for(int i=1;i<=n;i++)num[i]=(val[i]>=mid?1:0);
	buildtree(1,n,1);
	for(int i=1;i<=m;i++){
		int l=que[i].l,r=que[i].r;
		if(que[i].f==0){
			int k=query(l,r,1);
			update(r-k+1,r,1,1);update(l,r-k,0,1);
		}
		else{
			int k=query(l,r,1);
			update(l,l+k-1,1,1);update(l+k,r,0,1);
		}
	}
	return query(q,q,1);
}
void work(){
	int l=1,r=n,mid,ans=0;
	while(l<=r){
		mid=l+r>>1;
		if(check(mid)){ans=mid;l=mid+1;}
		else r=mid-1;
	}
	printf("%d\n",ans);
}
void init(){
	n=read();m=read();
	for(int i=1;i<=n;i++)val[i]=read();
	for(int i=1;i<=m;i++){que[i].f=read();que[i].l=read();que[i].r=read();}
	q=read();
}
int main(){
	init();
	work();
	return 0;
}

 

posted @ 2018-08-24 18:19  符拉迪沃斯托克  阅读(192)  评论(0编辑  收藏  举报
Live2D