题目描述:
达达现在碰到了一个棘手的问题,有N个整数需要排序。
达达手头能用的工具就是若干个双端队列。她从1到N需要依次处理这N个数,对于每个数,达达能做以下两件事:
1.新建一个双端队列,并将当前数作为这个队列中的唯一的数;
2.将当前数放入已有的队列的头之前或者尾之后。
对所有的数处理完成之后,达达将这些队列按一定的顺序连接起来后就可以得到一个非降的序列。
请你求出最少需要多少个双端序列。
输入格式
第一行输入整数N,代表整数的个数。接下来N行,每行包括一个整数Di,代表所需处理的整数。
输出格式
输出一个整数,代表最少需要的双端队列数。
数据范围
1≤N≤200000
输入样例:
6
3
6
0
9
6
3
输出样例:
2
分析:
我们知道,要想将几个双端队列直接拼成一个单调的序列,则这几个双端队列内部的顺序也一定是单调的,并且每个队列都是最后排好序的序列中的某一段连续的序列。想象一下任一个双端队列内部元素入队的顺序,一般情况下,下标最小的元素首先入队,之后入队的元素若比它小就插入到它前面,比它大就插入到它后面,则对于最终的任一个有序的队列,其中元素的下标一定是先递减再递增,也就是被称为满足单谷性质的序列。
知道这个性质有什么用呢?就是我们知道了最终有序序列中某一段要想仅占用一个队列,当且仅当他们的下标会满足这样的单谷性质,于是本题便由求最小的双端队列数转化为了求单谷序列最小的数目。值得注意的是,若原序列含有相同元素,则最后有序序列对应下标的顺序就不唯一了,我们只需要找到其中单谷序列最小的那个顺序即可。比如样例中的360963,排序后为033669,下标的一种顺序为205143,这样(20)(51)(43)便是三个单谷了,稍微调整下重复元素下标的顺序,使得下标排列为205413,(205)(413)便只需要两个队列了,可见,重复元素顺序的处理对最终结果尤为重要。
为了使得单谷序列最少,我们尽可能的使得数据上升下降趋势改变的越少越好,比如下标34的元素值相等,从下标为5的元素往后遍历,必然会有递减,543,则只是递减,534则有了波动。所以对于重复元素的下标,如果此时是上升趋势,后面重复元素最小的下标大于之前元素的下标,则继续上升趋势,否则要增加队列,改趋势为下降。若是下降趋势也是同理,重复元素最大的下标小于前面的下标,则继续下降,否则改趋势为上升。
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 200005;
pair<int,int> a[maxn];
int main(){
int n,ans = 1;
cin>>n;
for(int i = 0;i < n;i++){
cin>>a[i].first;
a[i].second = i;
}
sort(a,a + n);
int pre = maxn,dir = -1;
for(int i = 0;i < n;){
int j = i + 1;
while(j < n && a[j].first == a[i].first) j++;
int mi = a[i].second,ma = a[j - 1].second;
if(dir == -1){
if(ma < pre) pre = mi;
else{
dir = 1;
pre = ma;
}
}
else{
if(mi > pre) pre = ma;
else{
dir = -1;
pre = mi;
ans++;
}
}
i = j;
}
cout<<ans<<endl;
return 0;
}