[HEOI2016/TJOI2016]排序
题目描述
在2016年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他。这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q位置上的数字。
输入输出格式
输入格式:
输入数据的第一行为两个整数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
输出格式:
输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第q位置上的数字。
输入输出样例
6 3 1 6 2 5 3 4 0 1 4 1 3 6 0 2 4 3
5
说明
河北省选2016第一天第二题。原题的时限为6s,但是洛谷上是1s,所以洛谷的数据中,对于30%的数据,有 n,m<=1000,对于100%的数据,有 n,m<=30000
题解:
传说中的二分+01线段树。
题目中每次将某段区间按升序或降序排序,考虑暴力,如果我们每次都暴力排一遍序,那么每次就是nlogn的复杂度,显然是承受不了的。
试想如果要我们排序的序列是一个01序列,那么如果是升序,我们就只要将所有的1放到最后面,如果是降序,我们就只要将所有的1放到最前面。这样的复杂度是logn的。
二分一个答案,将序列中大于等于这个答案的数赋值为1,用01线段树模拟一遍所有的排序。
如果最后查询的位置为1,那么就说明这个位置的数大于等于mid;如果最后查询的位置为0,那么就说明这个位置的数小于mid,就可以根据这个缩小答案范围。
1 //Never forget why you start 2 #include<iostream> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cstring> 6 #include<cmath> 7 #include<algorithm> 8 #define ll(x) (x<<1) 9 #define rr(x) (x<<1|1) 10 using namespace std; 11 int n,a[100005],m,t1[100005],t2[100005],t3[100005],q,ans; 12 void init(){ 13 int i; 14 scanf("%d%d",&n,&m); 15 for(i=1;i<=n;i++)scanf("%d",&a[i]); 16 for(i=1;i<=m;i++){ 17 scanf("%d%d%d",&t1[i],&t2[i],&t3[i]); 18 } 19 scanf("%d",&q); 20 } 21 int sgm[400005],lazy[400005]; 22 void push_up(int root,int left,int right){ 23 sgm[root]=sgm[ll(root)]+sgm[rr(root)]; 24 } 25 void build(int root,int left,int right){ 26 if(left==right){ 27 sgm[root]=(a[left]>=ans); 28 return; 29 } 30 if(left>right)return; 31 int mid=(left+right)>>1; 32 build(ll(root),left,mid); 33 build(rr(root),mid+1,right); 34 push_up(root,left,right); 35 } 36 void push_down(int root,int left,int right){ 37 if(lazy[root]==-1)return; 38 int mid=(left+right)>>1; 39 sgm[ll(root)]=(mid-left+1)*lazy[root]; 40 sgm[rr(root)]=(right-mid)*lazy[root]; 41 lazy[ll(root)]=lazy[root]; 42 lazy[rr(root)]=lazy[root]; 43 lazy[root]=-1; 44 } 45 void insert(int root,int left,int right,int l,int r,int v){ 46 if(l<=left&&right<=r){ 47 sgm[root]=(right-left+1)*v; 48 lazy[root]=v; 49 return; 50 } 51 if(l>right||r<left)return; 52 push_down(root,left,right); 53 int mid=(left+right)>>1; 54 if(l<=mid)insert(ll(root),left,mid,l,r,v); 55 if(mid<r)insert(rr(root),mid+1,right,l,r,v); 56 push_up(root,left,right); 57 } 58 int query(int root,int left,int right,int l,int r){ 59 if(l<=left&&right<=r){ 60 return sgm[root]; 61 } 62 if(l>right||r<left)return 0; 63 push_down(root,left,right); 64 int mid=(left+right)>>1; 65 return query(ll(root),left,mid,l,r)+query(rr(root),mid+1,right,l,r); 66 } 67 bool judge(){ 68 build(1,1,n); 69 memset(lazy,-1,sizeof(lazy)); 70 for(int i=1;i<=m;i++){ 71 int l=t2[i],r=t3[i],tot1=query(1,1,n,l,r),tot0=(r-l+1)-tot1;//0的个数和1的个数 72 if(t1[i]==0){//升序 73 insert(1,1,n,l,l+tot0-1,0); 74 insert(1,1,n,l+tot0,r,1); 75 } 76 else{ 77 insert(1,1,n,l,l+tot1-1,1); 78 insert(1,1,n,l+tot1,r,0); 79 } 80 } 81 if(query(1,1,n,q,q))return false; 82 else return true; 83 } 84 int main(){ 85 init(); 86 int l=1,r=n,mid; 87 while(l<=r){ 88 ans=(l+r)>>1; 89 if(judge())r=ans-1; 90 else mid=ans,l=ans+1; 91 } 92 printf("%d\n",mid); 93 return 0; 94 }