CF 2081B Balancing
这个题应该是一个优美题的,但是被我线段树优化 dp 力大砖飞冲过去了(。
还是记录一下正经做法。
首先会发现因为选中的 \([l, r]\) 区间内部的大小关系都不会改变,于是每一步至多只会改变 \((l - 1, l), (r, r + 1)\) 两处的符号。
那么这意味着每一次选择至多使两个 \(>\) 变为 \(<\),那么记 \(c = \sum\limits_{i = 1}^{n - 1} [a_i > a_{i + 1}]\),答案有下界 \(\lceil\frac{c}{2}\rceil\)。
不过这个下界有可能取不到,尝试造一些反例:
发现 \(a = [0, -1, 1, 0]\) 时答案就不能取到 \(1\),因为 \(c = 2\) 就限定了只能操作 \([2, 3]\),但左右都为 \(0\) 无论怎么操作也不可能夹在之间。
经过反例的观察,会发现当 \(c\) 为偶数时,要达到答案下界就必须要让每一步选择都能减少 \(2\) 个 \(>\),那么第一个 \(>\) 左侧的元素和最后一个 \(>\) 右侧的元素永远不会改变(此时需要满足 \(c\ge 2\))。
于是记第一个 \(>\) 所在的位置是 \(a_l > a_{l + 1}\),最后一个 \(>\) 所在的位置是 \(a_{r - 1} > a_r\),\([l + 1, r - 1]\) 之间的元素若连 \(a_{l} + 1, a_{l} + 2, a_{l} + 3, \cdots\) 放下去都不合法,即 \(a_l + r - l > a_r\) 时,一定不会取到答案下界,至少要多出一步。
剩余的情况中一定能保证 \([l + 1, r - 1]\) 中的元素可以排布下,于是不妨猜测答案就是下界 \(+\) 是否是特殊情况。
不妨先来考虑看起来最极端的情况:\(c\) 为偶数且不为特殊情况。
考虑根据 \(c\) 分讨:
-
\(c = 0\),显然正确。
-
\(c = 2\),直接排列成 \(a_l + 1, a_l + 2, \cdots\) 即可。
-
\(c\ge 4\),此时直接去除第 \(1, 2\) 个 \(>\) 并不能保证能够操作,于是尝试去除第 \(1, 3\) 个 \(>\)。
对于第 \(1\) 个 \(>\) 和第 \(2\) 个 \(>\) 之间的段,就可以直接换成 \(a_l + 1, a_l + 2, \cdots\)。
对于第 \(2\) 个 \(>\) 和第 \(3\) 个 \(>\) 之间的段,发现限制只形如开头末尾要 \(<\) 某个值,于是可以直接替换为 \(-\operatorname{inf}, -\operatorname{inf} + 1, \cdots\)。容易验证此时依然不是特殊情况,递归构造即可。
那么 \(c\) 为奇数时,就有一个符号并不要求是 \(>\),那么可以考虑在第一步只删掉第 \(2\) 个 \(>\),选取 \(1\) 到第 \(2\) 个之间 \(>\) 的部分,都减掉一个极大值,就来到了 \(c\) 为偶数且不为特殊情况的情况。
对于特殊情况,那么这直接多了一步,就可以类似 \(c\) 为奇数,第一步删掉第 \(2\) 个 \(>\),然后递归到了 \(c\) 为奇数的情况。
时间复杂度 \(\mathcal{O}(n)\)。
#include <bits/stdc++.h>
constexpr int N = 2e5 + 10;
int n, a[N];
inline void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
int cnt = 0;
for (int i = 1; i < n; i++) {
cnt += a[i] > a[i + 1];
}
int ans = (cnt + 1) / 2;
if (cnt % 2 == 0 && cnt) {
int l = 1, r = n;
while (a[l] < a[l + 1]) {
l++;
}
while (a[r - 1] < a[r]) {
r--;
}
ans += a[l] + r - l > a[r];
}
printf("%d\n", ans);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
浙公网安备 33010602011771号