欢迎来到下蛋爷之家|

下蛋爷

园龄:4年2个月粉丝:8关注:23

[AGC040E] Prefix Suffix Addition 题解

Description

你有一个长为 N 的序列 x1,x2,,xN,一开始全部为 0,你现在可以以任意顺序进行任意次以下两种操作:

  1. 选定整数 k(1kN) 与不下降非负序列 c1,c2,,ck,对所有 1ik,令 xi 加上 ci
  2. 选定整数 k(1kN) 与不上升非负序列 c1,c2,,ck,对所有 1ik,令 xNk+i 加上 ci

问最少进行多少次操作使得最后对任意 ixi=Ai

1N2×105,1Ai109

Solution

首先如果只能做 1 操作,答案就是极长不降段的个数。只有 2 操作答案就是极长不升段的个数。

这启发我们将每个 Ai 拆成 aibi,分别表示来自 1 操作和 2 操作的贡献,答案即为:i=1N+1([ai<ai1]+[bi>bi1])

考虑 dp。

fi,j 表示考虑了前 i 个数,满足 ai=j,bi=Aij 的最小操作数。

直接转移是 O(nV) 的,但是注意到 fi,j 不升,且 fi,aifi,02,所以用记录一下三种值对应的区间即可。

时间复杂度:O(n)/O(nlogV)

Code

#include <bits/stdc++.h>
// #define int int64_t
const int kMaxN = 2e5 + 5;
int n;
int a[kMaxN];
void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
// int val = 2, f0 = a[1], f1 = a[1] + 1, f2 = a[1] + 1;
int val = 0, f0 = 0, f1 = 1, f2 = 1;
std::function<int(int)> getreal = [&] (int x) -> int {
if (x >= f2) return 1e9;
if (x <= f0) return val;
else if (x <= f1) return val - 1;
else return val - 2;
};
for (int i = 1; i <= n + 1; ++i) {
int _val, _f0, _f1;
std::function<int(int, int)> getcoef = [&] (int v1, int v2) -> int {
return (v1 > v2) + (a[i - 1] - v1 < a[i] - v2);
};
std::function<int(int)> calc = [&] (int x) -> int {
return std::min({getreal(0) + getcoef(0, x), getreal(f0 + 1) + getcoef(f0 + 1, x), getreal(f1 + 1) + getcoef(f1 + 1, x)});
};
_val = calc(0), _f0 = 0;
int L = 0, R = a[i] + 1;
while (L + 1 < R) {
int mid = (L + R) >> 1;
if (calc(mid) >= _val) L = _f0 = mid;
else R = mid;
}
L = _f1 = _f0, R = a[i] + 1;
while (L + 1 < R) {
int mid = (L + R) >> 1;
if (calc(mid) >= _val - 1) L = _f1 = mid;
else R = mid;
}
// std::cerr << i << ' ' << _val << ' ' << _f0 << ' ' << _f1 << " : \n";
// for (int j = 0; j <= a[i]; ++j) std::cerr << calc(j) << ' ';
// std::cerr << '\n';
// if (_f0 <= a[i]) assert(calc(_f0) == _val && calc(_f0 + 1) != _val);
// if (_f1 <= a[i]) assert(calc(_f1) == _val + 1);
val = _val, f0 = _f0, f1 = _f1, f2 = a[i] + 1;
}
std::cout << val << '\n';
}
int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}
posted @   下蛋爷  阅读(2)  评论(0编辑  收藏  举报
历史上的今天:
2024-02-20 P5163 WD与地图 题解
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起