题目链接
https://www.lydsy.com/JudgeOnline/problem.php?id=2457
分析
假设已按数值排好序,再写出每个数值对应的初始下标,“先单减再单增”、“单减”、“单增”的区间可用双端队列完成排序。
而相同的数值,对应初始下标是可以调换位置的,所以要通过调换,来用最少的双端队列实现。
尽量划分成“先单减再单增”的区间显然是最优的,处理出数值相同的区间后依次扫描,记录当前趋势;
每次尽量按原有趋势将当前区间加到上一区间之后,无法这样就改变当前趋势,趋势从单减开始不会更差。
AC代码
#include <cstdio>
#include <algorithm>
using namespace std;
inline int read() {
int num = 0, flag = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') flag = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
num = num * 10 + c - '0', c = getchar();
return flag * num;
}
const int maxn = 2e5 + 5;
struct Num {
int id, v;
bool operator < (const Num& rhs) const {
return v < rhs.v;
}
} a[maxn];
struct Section {
int minv, maxv;
} section[maxn];
int main() {
int n = read(), cnt = 0;
for (int i = 1; i <= n; ++i) a[i].id = i, a[i].v = read();
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; ++i) {
if (i == 1 || a[i].v != a[i - 1].v) {
section[++cnt].minv = a[i].id;
section[cnt].maxv = a[i].id;
} else {
section[cnt].minv = min(section[cnt].minv, a[i].id);
section[cnt].maxv = max(section[cnt].maxv, a[i].id);
}
}
int dir = 0, ans = 1;
for (int i = 2; i <= cnt; ++i) {
if (!dir && section[i].maxv > section[i - 1].minv) dir = 1;
else if (dir && section[i].minv < section[i - 1].maxv)
dir = 0, ++ans;
}
printf("%d", ans);
return 0;
}