模拟赛写了乱搞,结果还挂分了 \(40 \rightarrow 26\)。看题解感觉很 tricky,写一下笔记。
题意
给定一个括号串 \(S\),每个位置有颜色:蓝/绿/红。
你需要改变 \(S\) 一些位置的括号(不改变颜色),使得:
- 整个串是合法括号串。
- 删掉某个颜色后的串也是合法括号串。
\(|S| \le 10^7\)
思路
首先考虑一个合法括号串的判定,自然要满足下面三个条件:
- 任意一个前缀中
(的数量不少于)。 - 任意一个后缀中
)的数量不少于(。 - 整个串中
(、)的数量相等。
以上三个条件中任意两个成立都能说明这个串是合法的。
接着观察题中的信息,可以发现第二个条件蕴含第一个条件,证明:套路地将 ( 看作 \(1\),) 看作 \(-1\)。对于任意前缀 \(i\),设 \(1 \sim i\) 中三色括号的前缀和分别为 \(a, b, c\),则第二个条件等价于 \(a + b \ge 0, a + c \ge 0, b + c \ge 0\),则三者相加得到 \(2(a + b + c) \ge 0\)。后缀也是同理的,于是推出第一个条件。
考虑一个同色的括号串怎么求答案,求法有两种:
- 先满足条件 1,再满足条件 3。
做法:从左往右扫,每次一旦前缀和小于 \(0\) 就将最靠前的)改成(,最后还需要把靠后的(改成)保证条件 3。 - 先满足条件 3,再满足条件 1。
做法:如果(的数量比)少,就先把前几个)改为(;否则把后几个(改为)。
接着依次找到第一个)与最后一个(,将其反转使其满足条件 1。
赛时用了求法 1,但是错的,做不出来,下面说一下为什么求法 1 不对。
错因
先说做法:
可以参考题解的做法写出关于 \(x_1, x_2, x_3\) 的不等式并用线性规划解出。
设 \(x_i\) 表示将多少个颜色为 \(i\) 的 ( 改为了 )。对于一个前缀 \(j\),前缀和会增加 \(\min(x_i, A_{i, j})\),其中 \(A_{i, j}\) 表示有 \(1, 2, \ldots, j\) 中有多少颜色为 \(i\) 的 )。再设 \(B_{i, j}\) 表示 \(1, 2, \ldots, j\) 中颜色为 \(i\) 的括号串的前缀和。合法括号串需要满足(假设删掉的颜色为 \(k\))
在此基础上,最小化
但是我们发现这个式子很不对啊,因为不能保证贪心地改后缀不会影响不等式的成立。
所以理应还有设置 \(y_1, y_2, y_3\),表示后缀将多少个 ( 改为 ),那就是一个 6 元的线性规划,复杂度错了,还很难写,正确性也不保对。(我没继续想了,应该不行)大概是可被题解做法平替的。
接下来说说题解的做法。考虑求法 2,观察到每种颜色内 ( 和 ) 一定相等。证明:设每种颜色中 ( 数量减去 ) 数量的值为 \(a, b, c\),则我们要证 \(a = b = c = 0\),根据题中条件有 \(a + b = b + c = a + c = 0\),那加起来就是 \(2(a + b + c) = 0\),接着减一下就能得到 \(a, b, c\) 都等于 \(0\) 了。
于是可以对每种颜色提前做好第一步:平衡 ( 和 ) 的数量。贪心的把前几个 ) 改成 (,或者把后几个 ( 改成 ) 是最优的。
下面的括号串都是指经过了平衡的括号串。
接下来做第二步,设 \(x_i\) 表示颜色 \(i\) 操作了几次,\(A_{i, j}\) 表示 \([1, j]\) 中有多少颜色为 \(i\) 的 ),\(B_{i, j}\) 表示 \([j + 1, n]\) 中有多少颜色为 \(i\) 的 (,\(C_{i, j}\) 表示 \([1, j]\) 中颜色为 \(i\) 的括号的前缀和。可以写出式子
然后就写成了若干个关于 \(x_1, x_2, x_3\) 的不等式,目标是最小化 \(x_1 + x_2 + x_3\)。
那线性规划就行了,复杂度 \(O(n)\)。
代码:
#include <bits/stdc++.h>using i64 = long long;
using namespace std;constexpr int N = 1E7 + 5;int n, c[N];
string s, t;
int A[3][N], B[3][N], C[3][N];
int X[3], Y[3], R[3];int ans;int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cin >> s >> t;n = s.size();s = '#' + s;t = '#' + t;array<int, 3> cnt{0, 0, 0};for (int i = 1; i <= n; ++i) {c[i] = t[i] - '1';cnt[c[i]] += (s[i] == '(' ? 1 : -1);}for (int j = 0; j < 3; ++j) {ans += abs(cnt[j]) / 2;if (cnt[j] < 0) {for (int i = 1; cnt[j] && i <= n; ++i) {if (s[i] == ')' && c[i] == j) {s[i] = '(';cnt[j] += 2;}}} else if (cnt[j] > 0) {for (int i = n; cnt[j] && i >= 1; --i) {if (s[i] == '(' && c[i] == j) {s[i] = ')';cnt[j] -= 2;}}}}for (int i = 1; i <= n; ++i) {for (int j = 0; j < 3; ++j) {A[j][i] = A[j][i - 1] + (j == c[i]) * (s[i] == ')');}}for (int i = n; i >= 1; --i) {for (int j = 0; j < 3; ++j) {B[j][i] = B[j][i + 1] + (j == c[i]) * (s[i] == '(');}}for (int i = 1; i <= n; ++i) {for (int j = 0; j < 3; ++j) {C[j][i] = C[j][i - 1] + (j == c[i]) * (s[i] == '(' ? 1 : -1);}}for (int j = 0; j < 3; ++j) {int nj = (j == 2 ? 0 : j + 1);for (int i = 1; i <= n; ++i) {//2x_i + 2min(A[nj][i], B[nj][i]) + C[j][i] + C[nj][i] >= 0//2x_i + 2x_j + C[j][i] + C[nj][i] >= 0int l1 = -(2 * min(A[nj][i], B[nj][i]) + C[j][i] + C[nj][i]);int l2 = -(2 * min(A[j][i], B[j][i]) + C[j][i] + C[nj][i]);X[j] = max(X[j], (l1 + 1) / 2);X[nj] = max(X[nj], (l2 + 1) / 2);Y[j] = max(Y[j], (-C[j][i] - C[nj][i] + 1) / 2);}}int D = 0;for (int j = 0; j < 3; ++j) {int nj = (j + 1) % 3;R[j] = max(0, Y[j] - X[j] - X[nj]);D = max(D, R[j]);}D = max(D, (R[0] + R[1] + R[2] + 1) / 2);cout << ans + 2 * (D + X[0] + X[1] + X[2]) << "\n";return 0;
}
