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]排序——二分答案

bzoj千题计划128:bzoj4552: [Tjoi2016&Heoi2016]排序

Bzoj4552: [Tjoi2016&Heoi2016]排序