Solution
这里是博客:Tenil,刚刚用上了 JS,不妨看看?
题意
原题链接
给定数列 \(a_N\),按以下要求分为 \(n\) 组:
- 每组中的数单调不降。
- 每组中的数在原数列中的下标单调递减/单调递增/先递减再递增。(思考一下双向队列插入值的过程显然有:越往两端的越后入队)
- 存在一种方法,使所有分组拼接后整体单调不降。
求最小的 \(n\)。
分析
题目要求最终序列不降,所以断不可以按照输入顺序处理(所以 Sherry 才觉得棘手呢)。为什么呢?考虑一下样例:
3 6 0 9 6 3
如果直接按照输入顺序接,显然可以有分组方法: \([0,3,6,9],[3,6]\),不满足要求 \(3\)。其实就是在不知道所有数的情况下,可能把 \(l,r,mid(l < mid < r)\) 的 \(l,r\) 和 \(mid\) 分在了两组,导致无解。
为了避免无解的情况,一个很明显的策略是把所有数读入不降序排序后再处理,于是要求 \(1\) 和 \(3\) 容易满足,接下来问题就在要求 \(2\):为了满足下标的要求,在排序时记录下标,按排序后的顺序逐位验证要求 \(2\),不满足则 \(n\gets n+1\) 即可,数据处理流程如下:
3 6 0 9 6 3\\初始数据
0 3 3 6 6 9\\排序后
3 1 6 2 5 4\\下标,验证此数组计数
还需注意两个小问题:
-
初始按照什么单调性来?
一个完整的队列应是先递增再递减的,所以初始状态应为递减。
-
相等的数怎么处理?
相等的数是完全初下标外等价的,相当于可以任意交换,而且一定要放在一组(不然无解或可能不是最小),故相等的数下标自身须有序,即相等的数放在一起,排序后考虑。而内部有序,其实最终只需考虑端点值即可。
3 [1 6] [2 5] 4\\相等的数一起考虑
实现
全部读入,记录下标后升序排序,全部扫一遍,相等的放一起记录端点值,转化为拼接区间。变量记录当前单调性,拼接时:还满足则继续,不满足则反转单调性,当转为递减时队列数增加一。
Code
#include <iostream>
#include <cstdio>
#include <cctype>
#include <algorithm>using namespace std;typedef long long ll;inline ll fr() {ll x=0,f=1;char c=getchar();while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}while(isdigit(c)) {x=(x<<1)+(x<<3)+(c^48);c=getchar();}return x*f;
}const int maxn=2e5+1000;struct node {ll x,tag;
}num[maxn];
struct intva{ll l=1145141919,r=-1145141919;//这个初始值还没炸过(笑)
}qu[maxn];
ll n,ans=1;
bool down=true;inline bool cmp1(node n1,node n2) {return n1.x<n2.x;}int main() {n=fr();for(register int i = 1; i <= n; i++) {num[i].x=fr();num[i].tag=i;}sort(num+1,num+1+n,cmp1);ll now=num[1].x,cnt=1;//记得初始化qu[1].l=num[1].tag;qu[1].r=num[1].tag;for(register int i = 2; i <= n; i++) {if(num[i].x!=now) {now=num[i].x;cnt++;i--;//就是换一个区间重新来}else {qu[cnt].l=min(qu[cnt].l,num[i].tag);qu[cnt].r=max(qu[cnt].r,num[i].tag);}}for(register int i = 2; i<= cnt; i++) {if(down) {if(qu[i-1].l<qu[i].r) {down=false;}}else {if(qu[i-1].r>qu[i].l) {down=true;ans++;}}}printf("%lld\n",ans);return 0;
}
后话
感觉有用,还请点个赞吧!