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- ext{区间1的数量}+1,r ight]$改为$1$,$left[l,r- ext{区间1的数量} ight]$改为$0$
最后再查询要询问的位置。
若要查询的位置为$1$,那么就增加其排名,即$r=mid-1$
反之,就降低这个数排名。
由于这个数列是$left[1,n ight]$的全排列,所以二分出的结果就是答案。
附代码:
#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 ",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; }