Codeforces 650D Zip-line 题解

LIS,太奇妙.

思路

考虑替换序列中的一个元素对 LIS 的影响.设原序列的 LIS 长度为 kk,由于我们每次只会替换一个元素,所以对于替换后的序列,LIS 长度只有 33 种取值:k1k - 1kkk+1k + 1

考虑对于询问 ii,替换后序列的 LIS 的长度何时会取到这些值.假设我们已经计算出了包含 bib_i 的 LIS 的长度 tt

  1. tkt \ge k,那么答案显然为 max{t,k}\max\{t, k\}
  2. t<kt < k,我们发现答案为 tt 的充要条件是替换 haih_{a_i} 这一操作破坏了原序列中所有 LIS,否则答案仍然为 kk.这一条件等价于对于序列中所有可能的 LIS,出现在 LIS 的某一位上的元素只有 haih_{a_i}

考虑如何计算 tt.我们可以预处理出 fif_igig_i,分别表示以 hih_i 结束的 LIS 长度和以 hih_i 开头的 LIS 长度.考虑用 bib_i 连接两个上升序列,得到 LIS,我们有 t=maxj<ai,hj<bifj+1+maxai<j,bi<hjgjt = \max\limits_{j < a_i, h_j < b_i} f_j + 1 + \max\limits_{a_i < j, b_i < h_j} g_j,这个 max\max 是二维数点的形式,扫描线一下就可以求出.

考虑如何判定 2 情况中答案是否为 tt.我们可以计算出原序列的 LIS 中,ii 位置上能够填的元素数量,若这个数量为 11,那么替换 ii 位置上的元素就会破坏所有的 LIS.如何计算出这个数量?枚举位置 ii,若 fi+gi1=kf_i + g_i - 1 = k,说明 hih_i 至少出现在了一个 LIS 中,且出现位置为 fif_i,用一个桶计数即可.

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <utility>
#include <vector>

inline int rd() {
int x = 0, f = 1, c = getchar();
while (((c - '0') | ('9' - c)) < 0)
f = c != '-', c = getchar();
while (((c - '0') | ('9' - c)) > 0)
x = x * 10 + c - '0', c = getchar();
return f ? x : -x;
}

using pii = std::pair<int, int>;
const int N = 1e6;

int n, m, a[N + 5];
int b[N + 5], bct;

std::vector<pii> q[N + 5];
int ans[N + 5];

namespace fwt {
int t[N + 5];
inline int lowbit(int x) { return x & (-x); }
inline void upd(int x, int d) {
for (int i = x; i <= bct; i += lowbit(i)) {
t[i] = std::max(t[i], d);
}
}
inline int que(int x) {
int res = 0;
for (int i = x; i >= 1; i -= lowbit(i)) {
res = std::max(res, t[i]);
}
return res;
}
inline void init() { memset(t, 0, sizeof(t)); }
}; // namespace fwt

int f[N + 5], g[N + 5], h[N + 5];

int pfx[N + 5], sfx[N + 5];

int main() {
n = rd(), m = rd();
for (int i = 1; i <= n; i++) {
a[i] = rd();
b[++bct] = a[i];
}
for (int i = 1; i <= m; i++) {
int x = rd(), d = rd();
q[x].emplace_back(d, i);
b[++bct] = d;
}
std::sort(b + 1, b + 1 + bct);
bct = std::unique(b + 1, b + 1 + bct) - b - 1;
for (int i = 1; i <= n; i++) {
a[i] = std::lower_bound(b + 1, b + 1 + bct, a[i]) - b;
for (auto &[x, _] : q[i]) {
x = std::lower_bound(b + 1, b + 1 + bct, x) - b;
}
}

fwt::init();
for (int i = 1; i <= n; i++) {
f[i] = fwt::que(a[i] - 1) + 1;
fwt::upd(a[i], f[i]);
}
fwt::init();
for (int i = n; i >= 1; i--) {
g[i] = fwt::que(bct - a[i]) + 1;
fwt::upd(bct - a[i] + 1, g[i]);
}

int k = 0;
for (int i = 1; i <= n; i++) k = std::max(k, f[i] + g[i] - 1);
for (int i = 1; i <= n; i++) if (f[i] + g[i] - 1 == k) h[f[i]]++;
fwt::init();
for (int i = 1; i <= n; i++) {
for (auto [x, id] : q[i]) {
pfx[id] = fwt::que(x - 1);
}
fwt::upd(a[i], f[i]);
}
fwt::init();
for (int i = n; i >= 1; i--) {
for (auto [x, id] : q[i]) {
sfx[id] = fwt::que(bct - x);
}
fwt::upd(bct - a[i] + 1, g[i]);
}

for (int i = 1; i <= n; i++) {
for (auto [x, id] : q[i]) {
if (pfx[id] + sfx[id] + 1 < k && f[i] + g[i] - 1 == k && h[f[i]] == 1) {
ans[id] = k - 1;
} else ans[id] = std::max(pfx[id] + sfx[id] + 1, k);
}
}
for (int i = 1; i <= m; i++) printf("%d\n", ans[i]);
return 0;
}