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
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;
}

浙公网安备 33010602011771号