F - Chmax
F - Chmax
Problem Statement
You are given a positive integer $N$ and a permutation $P=(P_1,P_2,\ldots,P_N)$ of $(1,2,\ldots,N)$.
There are variables $x,y,c$. Initially, $x=y=c=0$.
For $k=1,2,\ldots,N$ in this order, you perform one of the following operations:
- Operation $1$: Increase $c$ by $1$ if $x < P_k$. Then, replace $x$ with $\max(x,P_k)$.
- Operation $2$: Increase $c$ by $1$ if $y < P_k$. Then, replace $y$ with $\max(y,P_k)$.
Find the maximum possible final value of $c$.
Constraints
- $1\le N\le 5\times 10^5$
- $P$ is a permutation of $(1,2,\ldots,N)$.
- All input values are integers.
Input
The input is given from Standard Input in the following format:
$N$
$P_1$ $P_2$ $\ldots$ $P_N$
Output
Output the answer.
Sample Input 1
5
4 3 1 2 5
Sample Output 1
4
By performing the operations as follows, you can achieve $c=4$:
- For $k=1$: Perform operation $1$. Then, $(x,y,c)=(4,0,1)$.
- For $k=2$: Perform operation $1$. Then, $(x,y,c)=(4,0,1)$.
- For $k=3$: Perform operation $2$. Then, $(x,y,c)=(4,1,2)$.
- For $k=4$: Perform operation $2$. Then, $(x,y,c)=(4,2,3)$.
- For $k=5$: Perform operation $2$. Then, $(x,y,c)=(4,5,4)$.
$c$ cannot be made greater than $4$ no matter how you perform the operations, so output $4$.
Sample Input 2
6
6 5 4 3 2 1
Sample Output 2
2
Sample Input 3
9
3 6 5 2 7 8 9 1 4
Sample Output 3
7
解题思路
先来点反思,一开始错误地把题目归约成了求两个不相交递增子序列的最大长度和问题,结果不仅发现不会做(问了下 LLM 才知道要用 RSK/Greene 定理,芝士什么.jpg),而且这思路本身就是错的。按照求递增子序列的思路,通常会先求出递增子序列,再把没被选中的元素随意插入到间隙里。但本题要求我们必须按顺序处理每一个元素,因此无法忽略最开始的元素 $P_1$。如果某个子序列没有选中 $P_1$,$P_1$ 依然会被强制并入这个序列,而 $P_1$ 的值可能很大,就会直接阻断后续较小元素在该序列上继续递增的机会。
举个简单的例子,有数组 $[6, 3, 5, 2, 4, 1]$。如果按题目的要求来做,能取得的最大结果是 $3$(对 $6$ 执行操作 $1$,其余元素依次执行操作 $2$)。而如果按两个不相交递增子序列的最大长度和来求,会得到 $[3, 5]$ 和 $[2, 4]$ 这两个子序列,长度和为 $4$。矛盾的地方就在于,第一个元素 $6$ 是不能被忽略的,它必须被放入其中一个序列。而一旦放入 $6$ 就会直接占据该序列的末尾,阻断后续较小元素(如 $3$、$2$)在这个序列上继续递增的机会,导致实际结果无法取得 $4$。
下面给出本题做法。看完本题,第一反应往往是状态数为 $O(n^3)$ 的暴力 dp,但又很容易下意识否决这种做法。然而本题的做法还真是从这个朴素的 dp 出发,一步步优化到 $O(n\log n)$。
定义 $f(i, j, k)$ 表示执行完前 $i$ 步后 $x = j$ 且 $y = k$ 的所有方案中得分的最大值,这样定义的状态数为 $O(n^3)$,显然不可行。记 $M_i = \max_{1 \leq k \leq i}\{P_k\}$,注意到一个关键性质:
引理 $1$:执行完前 $i$ 步后,$x$ 和 $y$ 中必定有一个等于 $M_i$。这是因为每一步 $k$ 中,必定执行 $x \leftarrow \max\{x, P_k\}$ 或 $y \leftarrow \max\{y, P_k\}$ 之一,因此整体的最大值 $\max\{x, y\}$ 必然更新为 $\max\{\max\{x, y\}, P_k\}$。对步数做数学归纳即可知道,执行完前 $i$ 步后恒有 $\max\{x, y\} = \max_{1 \leq k \leq i}\{P_k\} = M_i$。
有了这条引理,我们便只需追踪较小变量 $\min\{x, y\}$,从而将状态减少一维:定义 $f(i,p)$ 表示执行完前 $i$ 步后 $\min(x, y) = p$ 的所有方案中得分的最大值,边界为 $f(0,0)=0$。第 $i$ 步的操作,本质上是选择把 $P_i$ 并入第 $i-1$ 步结束后的较大变量还是较小变量。而这两种选择具体会带来怎样的得分与状态变化,则取决于 $P_i$ 与 $M_{i-1}$ 的大小关系。由于 $P$ 是排列,故恒有 $P_i \neq M_{i-1}$,因此下面按 $P_i < M_{i-1}$ 与 $P_i > M_{i-1}$ 两种情况分别讨论转移方程。
当 $P_i < M_{i-1}$ 时,$M_i=M_{i-1}$,较大变量不受影响。若这一步对较大变量操作,既不会得分,其值也不会改变,较小变量 $p$ 保持不变;若对较小变量 $p$ 操作,则仅当 $p<P_i$ 时才能得分 $+1$,此时 $p$ 变为 $P_i$,而当 $p > P_i$ 时既不得分,$p$ 的值也不变。综合可得,状态转移方程为 $$\begin{cases} f(i,p) = f(i-1,p), & p \in \{P_j \mid 1 \leq j < i\} \setminus \{M_{i-1}\} \\ f(i,P_i) = \max_{q<P_i}\{f(i-1,q)\} + 1, & p=P_i \end{cases}$$
其中前一项对应"操作较大变量"或"操作值大于 $P_i$ 的较小变量"这两种不得分的情形,后一项对应"操作值小于 $P_i$ 的较小变量,从而得分并使较小变量变为 $P_i$"这一情形。
当 $P_i > M_{i-1}$ 时,$P_i$ 成为新的最大值,此时无论操作哪个变量都能得分 $+1$:若对较大变量操作,则较小变量 $p$ 保持不变;若对较小变量操作,则较小变量变为 $P_i$(即新的最大值),原先的较大变量 $M_{i-1}$ 则退化为新的较小变量。状态转移方程为 $$ \begin{cases} f(i,p) = f(i-1,p) + 1, & p \in \{P_j \mid 1 \leq j < i\} \setminus \{M_{i-1}\} \\ f(i,M_{i-1}) = \max_{q < M_{i-1}}\left\{f(i-1,q)\right\} + 1, & p = M_{i-1} \end{cases}$$
其中前一项对应"操作较大变量"的情形,后一项对应"操作较小变量"的情形。进一步观察可以发现,这两种选择虽然得分相同,但前者使较小变量的取值保持为 $p$,后者使较小变量的取值变为 $M_{i-1}$,而 $p$ 严格小于 $M_{i-1}$。较小变量的取值越小,对后续显然越有利,所以操作较大变量总是不劣,对此不必再考虑操作较小变量。因此,转移方程可以简化为 $$f(i,p) = f(i-1,p) + 1, \quad p \in \{P_j \mid 1 \leq j < i\} \setminus \{M_{i-1}\}$$
直接暴力转移的话时间复杂度是 $O(n^2)$,仍不可行。进一步观察转移方程可以发现,其中只涉及单点赋值、前缀最大值查询与区间加操作,因此可以去掉表示阶段的第一维,改用值域线段树维护当前较小变量 $p$ 所对应的最大得分,并将不合法的状态值设为 $-\infty$。这样暴力转移就转化成了线段树上的操作,从而将复杂度降至 $O(n \log{n})$。
最终的答案即为 $\max_{0 \leq p < n} \{f(n, p)\}$。
AC 代码如下,时间复杂度为 $O(n \log{n})$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e5 + 5, INF = 0xc0c0c0c0;
int a[N];
struct Node {
int l, r, mx, add;
}tr[N * 4];
void build(int u, int l, int r) {
tr[u] = {l, r, INF};
if (l == r) {
if (!l) tr[u].mx = 0;
}
else {
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
tr[u].mx = max(tr[u << 1].mx, tr[u << 1 | 1].mx);
}
}
void upd(int u, int d) {
tr[u].mx += d;
tr[u].add += d;
}
void pushdown(int u) {
if (!tr[u].add) return;
upd(u << 1, tr[u].add);
upd(u << 1 | 1, tr[u].add);
tr[u].add = 0;
}
void modify(int u, int l, int r, int c, int d) {
if (tr[u].l >= l && tr[u].r <= r) {
if (c != INF) tr[u].mx = c;
if (d) upd(u, d);
}
else {
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify(u << 1, l, r, c, d);
if (r >= mid + 1) modify(u << 1 | 1, l, r, c, d);
tr[u].mx = max(tr[u << 1].mx, tr[u << 1 | 1].mx);
}
}
int query(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) return tr[u].mx;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (r <= mid) return query(u << 1, l, r);
if (l >= mid + 1) return query(u << 1 | 1, l, r);
return max(query(u << 1, l, r), query(u << 1 | 1, l, r));
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
build(1, 0, n);
for (int i = 1, mx = 1; i <= n; i++) {
if (a[i] < mx) modify(1, a[i], a[i], query(1, 0, a[i] - 1) + 1, 0);
else modify(1, 0, mx - 1, INF, 1);
mx = max(mx, a[i]);
}
cout << tr[1].mx;
return 0;
}
再提供另外一种贪心的做法,嗯赌就能过这一块。答案就是前缀最大值的个数,加上除去前缀最大值这些元素后剩下元素的 LIS 长度。博主是 baka,猜是不可能猜的,这辈子不可能猜中的.jpg。下面给出严谨证明(由 Claude Sonnet 5 Max 辅助完成)。
沿用前面的定义 $M_i = \max_{1 \leq k \leq i}\{P_k\}$,称满足 $P_i=M_i>M_{i-1}$ 的位置为前缀最大值位置,并把所有非前缀最大值位置上的 $P_i$ 按原顺序排成的序列记为 $Q$。要证明 $c$ 的最大值为 $c_{\max} = \left(n - |Q| \right) + \mathrm{LIS}(Q)$,思路是先证明任何方案所能取得的得分都不会超过 $c_{\max}$,再构造出一个恰好取得 $c_{\max}$ 的方案。定义 $m_i = \min\{x,y\}$(约定 $m_0 = 0$),表示执行完前 $i$ 步后 $x$ 与 $y$ 中的较小值。
引理 $2$:对于任意方案,恒有 $m_0\le m_1\le\cdots\le m_n$。这是因为每一步中,$x$ 要么保持不变,要么与 $P_i$ 取最大值,因此恒有 $x^{(i)} \geq x^{(i-1)}$;同理 $y^{(i)} \geq y^{(i-1)}$。两个逐点不减的数列,其逐点取最小值得到的数列显然也不减,故 $m_i$ 单调不减,引理得证。
引理 $3$(非前缀最大值位置的得分条件):对于 $P_i < M_{i-1}$ 的非前缀最大值位置 $i$,该步得分的充要条件是操作较小变量 $m_{i-1}$ 且 $m_{i-1} < P_i$。因为 $P_i < M_{i-1}$,操作较大变量无法得分,只有操作较小变量且满足 $m_{i-1} < P_i$ 时才能得分。此时较小变量更新为 $m_i = \max\{m_{i-1}, P_i\} = P_i$。
下面证明 $c \leq \left(n - |Q| \right) + \mathrm{LIS}(Q)$。我们把总得分拆成前缀最大值位置与非前缀最大值位置这两部分各自的贡献,分别估计上界。
对于前缀最大值位置 $i$,由引理 $1$ 可知,第 $i$ 步操作前恒有 $m_{i-1} < M_{i-1} < P_i$,因此无论这一步怎么操作都必然得分,与具体策略无关,这部分的贡献恒为前缀最大值位置的个数,即 $n - |Q|$。
再考虑某个具体方案在非前缀最大值位置中得分的下标,设依次为 $k_1 < \cdots < k_t$。由引理 $3$,在下标 $k_i$ 处得分时必有 $m_{k_i} = P_{k_i}$ 且 $P_{k_i} > m_{k_i - 1}$。又由引理 $2$ 的单调性可知 $m_{k_{i-1}} \leq m_{k_i - 1}$,再结合 $k_{i-1}$ 处得分时同样有 $P_{k_{i-1}}=m_{k_{i-1}}$,于是 $P_{k_{i-1}}=m_{k_{i-1}}\le m_{k_i - 1}<P_{k_i}$。依此递推即可得 $P_{k_1} < \cdots < P_{k_t}$,也就是说这些得分位置在 $Q$ 中构成一个严格递增子序列,故这部分的贡献满足 $t\le\mathrm{LIS}(Q)$。综合两部分即得任何方案的得分都满足 $c \leq \left(n - |Q| \right) + \mathrm{LIS}(Q)$。
下面证明可以构造出达到上界的方案。任取 $Q$ 的一个 LIS,下标满足 $k_1 < \cdots < k_l$。对这 $l$ 个位置执行操作 $2$,其余所有位置(包括全部前缀最大值位置)执行操作 $1$,这样一来 $x$ 始终是较大变量,$y$ 是较小变量。下面按位置类型逐一验证得分情况:
- 若第 $i$ 步处于前缀最大值位置,前面已经证明这一步必然得分,此时 $x$ 更新为 $P_i=M_i$。
- 若第 $i$ 步处于非前缀最大值位置,分两种情形讨论:
- 对于不属于 LIS 的位置,$x$ 的值恒为 $M_{i-1} > P_i$,因此 $x$ 不变,也不得分。
- 对于属于 LIS 的位置,由于 $y$ 只会在这些位置被操作,第 $j$ 个 LIS 位置处 $y$ 的值,正是上一个 LIS 位置留下的 $P_{k_{j-1}}$。又因为 LIS 严格递增,即 $P_{k_j}>P_{k_{j-1}}$,所以这一步必然得分。
于是该方案下前缀最大值位置与 LIS 位置全部都有得分,总得分恰好为 $\left(n - |Q| \right) + \mathrm{LIS}(Q)$,达到上界。命题得证(经典证明比代码长)。
AC 代码如下,时间复杂度为 $O(n \log{n})$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int lis(vector<int> &p) {
vector<int> q;
for (int i = 0; i < p.size(); i++) {
auto it = lower_bound(q.begin(), q.end(), p[i]);
if (it != q.end()) *it = p[i];
else q.push_back(p[i]);
}
return q.size();
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> q;
for (int i = 0, mx = 0; i < n; i++) {
int x;
cin >> x;
if (x < mx) q.push_back(x);
mx = max(mx, x);
}
cout << n - q.size() + lis(q);
return 0;
}
参考资料
Editorial - AtCoder Beginner Contest 468:https://atcoder.jp/contests/abc468/editorial/23738
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/22048421

浙公网安备 33010602011771号