[BZOJ4552]:[Tjoi2016&Heoi2016]排序(桶排序)

题目传送门


题目描述

在2016年,佳媛姐姐喜欢上了数字序列。

因而她经常研究关于序列的一些奇奇怪怪的问题,现在她在研究一个难题,需要你来帮助她。

这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q位置上的数字。


输入格式

输入数据的第一行为两个整数n和m。n表示序列的长度,m表示局部排序的次数。
第二行为n个整数,表示1到n的一个全排列。
接下来输入m行,每一行有三个整数op,l,r,op为0代表升序排序,op为1代表降序排序,l,r表示排序的区间。
最后输入一个整数q,q表示排序完之后询问的位置。


输出格式

输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第q位置上的数字。


样例

样例输入

6 3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3

样例输出

5


数据范围与提示

$1\leqslant n,m\leqslant {10}^5$

$1\leqslant q\leqslant n$


题解

它放在了“不打正解的我”这一板块,正解是线段树,时间复杂度$\Theta (m\log^2n)$。

确实,这道题我打的暴力。

首先考虑暴力sort,时间复杂度:$\Theta (m\times n\log n)$。

但是,$10^5$的数据范围显然跑不过,于是我便想到了一个并不常用的排序方法:桶排序。

这道题保证了这$n$个数是$n$的全排列,于是桶排序可行。

但是稳妥的桶排序在统计答案的时候需要扫整个区间,在$m$次询问后时间复杂度会变为$\Theta (n\times m)$,显然有不可做了。

那么我在往桶里放数的时候标记一下放进去的最大值和最小值,然后在往外拿的时候只扫描这个区间,成功卡过,甚至比某些打的不怎么优秀的线段树还要快。

码长和内存才是亮点。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int a[100001],t[100001];
void change0(int l,int r)//升序
{
	int maxn=0,minn=20020923,flag=l;
	for(int i=l;i<=r;i++)
	{
		t[a[i]]=1;
		minn=min(minn,a[i]);
		maxn=max(maxn,a[i]);
	}
	for(int i=minn;i<=maxn;i++)
		if(t[i])a[flag++]=i,t[i]=0;
}
void change1(int l,int r)//降序
{
	int maxn=0,minn=20020923,flag=l;
	for(int i=l;i<=r;i++)
	{
		t[a[i]]=1;
		minn=min(minn,a[i]);
		maxn=max(maxn,a[i]);
	}
	for(int i=maxn;i>=minn;i--)
		if(t[i])a[flag++]=i,t[i]=0;
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	while(m--)
	{
		int op,l,r;
		scanf("%d%d%d",&op,&l,&r);
		if(op)change1(l,r);
		else change0(l,r);
	}
	scanf("%d",&n);
	printf("%d",a[n]);
	return 0;
}

rp++

posted @ 2019-07-24 12:10  HEOI-动动  阅读(146)  评论(0编辑  收藏  举报