UOJ958 笔记

模拟赛写了乱搞,结果还挂分了 \(40 \rightarrow 26\)。看题解感觉很 tricky,写一下笔记。

题意

给定一个括号串 \(S\),每个位置有颜色:蓝/绿/红。

你需要改变 \(S\) 一些位置的括号(不改变颜色),使得:

  • 整个串是合法括号串。
  • 删掉某个颜色后的串也是合法括号串。

\(|S| \le 10^7\)

思路

首先考虑一个合法括号串的判定,自然要满足下面三个条件:

  1. 任意一个前缀中 ( 的数量不少于 )
  2. 任意一个后缀中 ) 的数量不少于 (
  3. 整个串中 () 的数量相等。

以上三个条件中任意两个成立都能说明这个串是合法的。

接着观察题中的信息,可以发现第二个条件蕴含第一个条件,证明:套路地将 ( 看作 \(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. 先满足条件 1,再满足条件 3。
    做法:从左往右扫,每次一旦前缀和小于 \(0\) 就将最靠前的 ) 改成 (,最后还需要把靠后的 ( 改成 ) 保证条件 3。
  2. 先满足条件 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\)

\[\sum_{i \ne k} 2 \min(x_i, A_{i, j}) + B_{i, j} \ge 0 \]

在此基础上,最小化

\[\sum_{i = 1}^3 2x_i + \frac{B_{i, n}}{2} \]

但是我们发现这个式子很不对啊,因为不能保证贪心地改后缀不会影响不等式的成立。

所以理应还有设置 \(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\) 的括号的前缀和。可以写出式子

\[\forall j, k, \sum_{i \ne k} 2\min(x_i, A_{i, j}, B_{i, j}) + C_{i, j} \ge 0 \]

然后就写成了若干个关于 \(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] >= 0
            int 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;
}
posted @ 2025-09-01 15:57  CTHOOH  阅读(56)  评论(0)    收藏  举报