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

題目傳送門


題目描述

在2016年,佳媛姐姐喜歡上了數字序列。php

於是她常常研究關於序列的一些奇奇怪怪的問題,如今她在研究一個難題,須要你來幫助她。c++

這個難題是這樣子的:給出一個1到n的全排列,如今對這個全排列序列進行m次局部排序,排序分爲兩種:1:(0,l,r)表示將區間[l,r]的數字升序排序2:(1,l,r)表示將區間[l,r]的數字降序排序最後詢問第q位置上的數字。spa


輸入格式

輸入數據的第一行爲兩個整數n和m。n表示序列的長度,m表示局部排序的次數。
第二行爲n個整數,表示1到n的一個全排列。
接下來輸入m行,每一行有三個整數op,l,r,op爲0表明升序排序,op爲1表明降序排序,l,r表示排序的區間。
最後輸入一個整數q,q表示排序完以後詢問的位置。
blog


輸出格式

輸出數據僅有一行,一個整數,表示按照順序將所有的部分排序結束後第q位置上的數字。排序


樣例

樣例輸入內存

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

樣例輸出it

5class


數據範圍與提示

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

相關文章
相關標籤/搜索