[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++