[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位置上的数字。

输入输出样例

输入样例#1: 复制
6 3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3
输出样例#1: 复制
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 }

 

posted @ 2018-01-14 20:44  kakakakakaka  阅读(498)  评论(0编辑  收藏  举报

Never forget why you start

//鼠标爆炸特效