比较神奇的题。
考虑如果只有第 \(1\) 种操作,答案显然就是 \(\sum\limits_{i = 0}^n [a_i > a_{i + 1}]\)。因为两个极长不降子段显然不可能通过一次操作消除,并且可以逐个消除极长不降子段达到这个下界。
现在有 \(2\) 种操作,不妨设第 \(1\) 种操作对 \(a_i\) 的贡献为 \(b_i\),第 \(2\) 种操作对 \(a_i\) 的贡献为 \(c_i\)(显然 \(a_i = b_i + c_i\))。那么答案就是 \(\sum\limits_{i = 0}^n [b_i > b_{i + 1}] + [c_i < c_{i + 1}]\)。
\(b_i, c_i\) 不确定,自然地想到一个初步的 dp,设 \(f_{i, j}\) 为考虑到第 \(i\) 位,\(b_i = j, c_i = a_i - j\) 的最小操作次数。有转移:
整理得:
初值 \(f_{0, 0} = 0\),答案为 \(f_{n + 1, 0}\)。
没办法直接数据结构优化,不妨观察这个转移形式的性质。
发现 \(f_{i, j}\) 单调不增,因为更大的 \(j\) 显然要加的更少。
又发现如果我们强制钦定 \(f_{i, 0}\) 从 \(f_{i, a_i}\) 的最优转移点转移过来,会得到 \(f_{i, 0} \le f_{i, a_i} + 2\)。
于是我们可以知道 \(f_i\) 被分成值相等的至多 \(3\) 段。然后我们维护这 \(O(1)\) 段即可。
注意到 \(f_{i, j}\) 一定是从 \(f_{i - 1}\) 的每一段的左端点转移过来(因为 \(k\) 增大,\(f_{i - 1, k}\) 不变,后面加的不会变少),所以如果我们知道 \(j\),我们可以 \(O(1)\) 计算出后面那坨最小值。
于是我们对于 \(f_i\) 的 \(O(1)\) 段,每一段知道左端点,二分找到其右端点即可。这样转移是 \(O(\log V)\) 的(其实可以分类讨论做到 \(O(1)\) 转移,但是我太懒了)。
code
// Problem: E - Prefix Suffix Addition
// Contest: AtCoder - AtCoder Grand Contest 040
// URL: https://atcoder.jp/contests/agc040/tasks/agc040_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 200100;
int n, a[maxn];
struct node {
int l, r, x;
node(int a = 0, int b = 0, int c = 0) : l(a), r(b), x(c) {}
};
vector<node> f[maxn];
inline int calc(int i, int x) {
int mn = 1e9;
for (node u : f[i - 1]) {
mn = min(mn, u.x + (x < u.l) + (x < u.l + a[i] - a[i - 1]));
}
return mn;
}
void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
f[0].pb(0, 0, 0);
for (int i = 1; i <= n + 1; ++i) {
for (int x = 0, y; x <= a[i]; x = y + 1) {
int l = x, r = a[i];
int t = calc(i, x);
while (l <= r) {
int mid = (l + r) >> 1;
if (calc(i, mid) == t) {
y = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
f[i].pb(x, y, t);
}
}
printf("%d\n", f[n + 1].back().x);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}
- Addition AtCoder Contest Prefix Suffixaddition atcoder contest prefix addition atcoder regular contest ptyhon prefix remame suffix permutation addition atcoder 159c contest programming beginner atcoder beginner atcoder contest 296 beginner atcoder contest 295 beginner atcoder contest abcde beginner atcoder contest 335 beginner atcoder contest 328