bzoj4552[Tjoi2016&Heoi2016]排序 二分+线段树
Posted GXZlegend
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4552[Tjoi2016&Heoi2016]排序 二分+线段树相关的知识,希望对你有一定的参考价值。
题目描述
给出一个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
题解
二分+线段树
直接求这个数是什么比较困难,但是考虑:如果是判定性问题,询问这个数是否大于等于k的话却十分简单。
并且显然具有单调性,因此可以二分。
二分答案mid,转化为判断q位置是否大于等于mid。考虑把大于等于mid的数看作1,小于mid的数看作0,那么每次降序排序相当于把区间中的1放到前面,0放到后面;升序排序相反。
因此可以使用线段树来实现这个过程。最后这个位置如果是1则可行,否则不可行。
时间复杂度$O(n\log^2n)$
#include <cstdio> #define N 100010 #define lson l , mid , x << 1 #define rson mid + 1 , r , x << 1 | 1 int a[N] , v[N] , sum[N << 2] , tag[N << 2] , opt[N] , x[N] , y[N]; inline void pushup(int x) { sum[x] = sum[x << 1] + sum[x << 1 | 1]; } inline void pushdown(int l , int r , int x) { if(~tag[x]) { int mid = (l + r) >> 1; sum[x << 1] = tag[x] * (mid - l + 1) , tag[x << 1] = tag[x]; sum[x << 1 | 1] = tag[x] * (r - mid) , tag[x << 1 | 1] = tag[x]; tag[x] = -1; } } void build(int l , int r , int x) { tag[x] = -1; if(l == r) { sum[x] = v[l]; return; } int mid = (l + r) >> 1; build(lson) , build(rson); pushup(x); } void update(int b , int e , int a , int l , int r , int x) { if(b <= l && r <= e) { sum[x] = a * (r - l + 1) , tag[x] = a; return; } pushdown(l , r , x); int mid = (l + r) >> 1; if(b <= mid) update(b , e , a , lson); if(e > mid) update(b , e , a , rson); pushup(x); } int query(int b , int e , int l , int r , int x) { if(b <= l && r <= e) return sum[x]; pushdown(l , r , x); int mid = (l + r) >> 1 , ans = 0; if(b <= mid) ans += query(b , e , lson); if(e > mid) ans += query(b , e , rson); return ans; } int main() { int n , m , q , i , l , r , mid , ans = 0 , t; scanf("%d%d" , &n , &m); for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]); for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%d" , &opt[i] , &x[i] , &y[i]); scanf("%d" , &q); l = 1 , r = n; while(l <= r) { mid = (l + r) >> 1; for(i = 1 ; i <= n ; i ++ ) v[i] = (a[i] >= mid); build(1 , n , 1); for(i = 1 ; i <= m ; i ++ ) { t = query(x[i] , y[i] , 1 , n , 1) , update(x[i] , y[i] , 0 , 1 , n , 1); if(t) { if(opt[i]) update(x[i] , x[i] + t - 1 , 1 , 1 , n , 1); else update(y[i] - t + 1 , y[i] , 1 , 1 , n , 1); } } if(query(q , q , 1 , n , 1)) ans = mid , l = mid + 1; else r = mid - 1; } printf("%d\n" , ans); return 0; }
以上是关于bzoj4552[Tjoi2016&Heoi2016]排序 二分+线段树的主要内容,如果未能解决你的问题,请参考以下文章
[BZOJ4552][TJOI2016&&HEOI2016]排序
BZOJ4552:[Tjoi2016&Heoi2016]排序
[BZOJ] 4552: [Tjoi2016&Heoi2016]排序 #二分+线段树+算法设计策略
bzoj 4552 [Tjoi2016&Heoi2016]排序——二分答案