【BZOJ4552】【HEOI2016】排序 [二分答案][线段树]
排序
Time Limit: 60 Sec Memory Limit: 256 MB[Submit][Status][Discuss]
Description
在2016年,佳媛姐姐喜欢上了数字序列。
因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他。
这个难题是这样子的:
给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:
1: (0,l,r)表示将区间[l,r]的数字升序排序
2: (1,l,r)表示将区间[l,r]的数字降序排序
最后询问第q位置上的数字。
Input
输入数据的第一行为两个整数n和m。
n表示序列的长度,m表示局部排序的次数。
第二行为n个整数,表示1到n的一个全排列。
接下来输入m行,每一行有三个整数op, l, r,
op为0代表升序排序,op为1代表降序排序, l, r 表示排序的区间。
最后输入一个整数q,q表示排序完之后询问的位置。
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
HINT
1 <= n <= 10^5,1 <= m <= 10^5, 1 <= q <= n。
Solution
我们先考虑如果权值很小的话怎么做,显然可以对每个权值开一个线段树维护在哪些位置出现过。
那么排序显然就是覆盖连续的一段。只要知道某一区间有几个这个权值即可。
但是这样显然是过不了的,于是我们考虑二分答案,把val >= mid的设为1,其余的设为0。
这样就把权值变成了0/1,那么显然我们按照以上操作,如果Q位置上是1说明mid<=Ans,还可以更大一点,否则说明mid>Ans。
只要支持区间求和以及区间覆盖0/1即可。
Code
1 #include<iostream> 2 #include<string> 3 #include<algorithm> 4 #include<cstdio> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cmath> 8 using namespace std; 9 typedef long long s64; 10 11 const int ONE = 400005; 12 const int MOD = 1e9 + 7; 13 14 int get() 15 { 16 int res = 1, Q = 1; char c; 17 while( (c = getchar()) < 48 || c > 57) 18 if(c == '-') Q = -1; 19 if(Q) res = c - 48; 20 while( (c = getchar()) >= 48 && c <= 57) 21 res = res * 10 + c - 48; 22 return res * Q; 23 } 24 25 int n, m, Q; 26 int a[ONE]; 27 int res, now; 28 29 struct power 30 { 31 struct point 32 { 33 int val, tag; 34 }Node[ONE]; 35 36 void Build(int i, int l, int r) 37 { 38 Node[i].tag = -1; 39 if(l == r) return; 40 int mid = l + r >> 1; 41 Build(i << 1, l, mid); 42 Build(i << 1 | 1, mid + 1, r); 43 } 44 45 int pushdown(int i, int l, int r) 46 { 47 int mid = l + r >> 1; 48 if(Node[i].tag != -1) 49 { 50 Node[i << 1].tag = Node[i].tag; 51 Node[i << 1].val = Node[i].tag * (mid - l + 1); 52 Node[i << 1 | 1].tag = Node[i].tag; 53 Node[i << 1 | 1].val = Node[i].tag * (r - (mid + 1) + 1); 54 Node[i].tag = -1; 55 } 56 } 57 58 void Update(int i, int l, int r, int L, int R, int x) 59 { 60 if(L > R) return; 61 if(L <= l && r <= R) 62 { 63 Node[i].tag = x; 64 Node[i].val = x * (r - l + 1); 65 return; 66 } 67 pushdown(i, l, r); 68 int mid = l + r >> 1; 69 if(L <= mid) Update(i << 1, l, mid, L, R, x); 70 if(mid + 1 <= R) Update(i << 1 | 1, mid + 1, r, L, R, x); 71 Node[i].val = Node[i << 1].val + Node[i << 1 | 1].val; 72 } 73 74 void Query(int i, int l, int r, int L, int R) 75 { 76 if(L > R) return; 77 if(L <= l && r <= R) 78 { 79 res += Node[i].val; 80 return; 81 } 82 pushdown(i, l, r); 83 int mid = l + r >> 1; 84 if(L <= mid) Query(i << 1, l, mid, L, R); 85 if(mid + 1 <= R) Query(i << 1 | 1, mid + 1, r, L, R); 86 } 87 }C[2]; 88 89 struct operate 90 { 91 int l, r, x; 92 }oper[ONE]; 93 94 void Modify(int id, int Left, int Right) 95 { 96 res = 0; 97 C[id].Query(1, 1, n, Left, Right); 98 C[id].Update(1, 1, n, Left, Right, 0); 99 C[id].Update(1, 1, n, now, now + res - 1, 1); 100 now += res; 101 } 102 103 int Check(int mid) 104 { 105 for(int i = 0; i <= 1; i++) 106 C[i].Node[1].tag = 0; 107 108 for(int i = 1; i <= n; i++) 109 C[a[i] >= mid].Update(1, 1, n, i, i, 1); 110 111 for(int i = 1; i <= m; i++) 112 { 113 now = oper[i].l; 114 if(oper[i].x == 0) for(int id = 0; id <= 1; id++) Modify(id, oper[i].l, oper[i].r); 115 if(oper[i].x == 1) for(int id = 1; id >= 0; id--) Modify(id, oper[i].l, oper[i].r); 116 } 117 118 res = 0, C[1].Query(1, 1, n, Q, Q); 119 return res; 120 } 121 122 int main() 123 { 124 n = get(); m = get(); 125 for(int i = 1; i <= n; i++) 126 a[i] = get(); 127 for(int i = 1; i <= m; i++) 128 oper[i].x = get(), oper[i].l = get(), oper[i].r = get(); 129 130 Q = get(); 131 int l = 1, r = n; 132 while(l < r - 1) 133 { 134 int mid = l + r >> 1; 135 if(Check(mid)) l = mid; 136 else r = mid; 137 } 138 139 if(Check(r)) printf("%d", r); 140 else printf("%d", l); 141 } 142