From f83f5c24bd6878ff15e693c2699e64b7f2f0462f Mon Sep 17 00:00:00 2001 From: Yawn-Sean <1900015431@pku.edu.cn> Date: Thu, 9 Jan 2025 10:38:44 +0800 Subject: [PATCH] Editorial for CF629C --- .../2025/01/0109/solution/cf629c.md | 99 ++++++++++++++++++- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/daily_problems/2025/01/0109/solution/cf629c.md b/daily_problems/2025/01/0109/solution/cf629c.md index 9ecb2ad3b5..7af611906c 100644 --- a/daily_problems/2025/01/0109/solution/cf629c.md +++ b/daily_problems/2025/01/0109/solution/cf629c.md @@ -4,7 +4,9 @@ 一个括号序列合法,等价于把左括号看成 $1$ ,右括号看成 $-1$ ,任意前缀和都非负,且整体和为 $0$ 。 -于是,假设前缀部分的和为 $x$ ,则其加上中间部分的最小前缀和 $y$ 后应当非负。 +于是,假设前缀部分的和为 $x$ ,则其加上中间部分的最小前缀和 $y$ 后应当非负。同时,整体和为 $0$ 即可。 + +前面部分唯一重要的信息就是长度还有和。后缀部分也是一样,所以考虑关于这件事 DP 。 我们考虑进行长度为 $i$ 的和为 $j$ 的前缀数量为 $dp[i][j]$ ,则这个 DP 的转移关系相当容易:考虑下一位是 $1$ 还是 $-1$ ,需要满足 $j$ 加上之非负,则进一步发生 $dp[i+1][j-1]$ 或 $dp[i+1][j+1]$ 的转移。 @@ -12,4 +14,97 @@ 我们可以枚举前缀的长度和数值和。则前缀加后缀的总长度等于 $n-m$ ,且整体和为 $0$ ,再加上前缀部分的和 $x$ 与中间部分的最小前缀和的和非负,即可完成前缀后缀两部分的选择。 -时间复杂度为 $\mathcal{O}((n-m)^2)$ 。 \ No newline at end of file +时间复杂度为 $\mathcal{O}((n-m)^2)$ 。 + +### 具体代码如下—— + +Python 做法如下—— + +```Python [] +def main(): + n, m = MII() + s = I() + + mod = 10 ** 9 + 7 + + dp = [[0] * 2001 for _ in range(2001)] + dp[0][0] = 1 + + for i in range(2000): + for j in range(i + 1): + if j: + dp[i + 1][j - 1] += dp[i][j] + if dp[i + 1][j - 1] >= mod: + dp[i + 1][j - 1] -= mod + dp[i + 1][j + 1] += dp[i][j] + if dp[i + 1][j + 1] >= mod: + dp[i + 1][j + 1] -= mod + + cur = 0 + cur_min = 0 + + for c in s: + if c == '(': cur += 1 + else: cur -= 1 + cur_min = fmin(cur, cur_min) + + ans = 0 + for i in range(n - m + 1): + for j in range(2001): + if j + cur_min >= 0 and 0 <= j + cur <= 2000: + ans += dp[i][j] * dp[n - m - i][j + cur] % mod + if ans >= mod: ans -= mod + + print(ans) +``` + +C++ 做法如下—— + +```cpp [] +int main() { + ios_base::sync_with_stdio(false); + cin.tie(0); + cout.tie(0); + + int n, m, mod = 1e9 + 7; + cin >> n >> m; + + string s; + cin >> s; + + vector> dp(2001, vector(2001, 0)); + dp[0][0] = 1; + + auto update = [&] (int &x, int y) -> void { + x += y; + if (x >= mod) x -= mod; + }; + + for (int i = 0; i < 2000; i ++) { + for (int j = 0; j <= i; j ++) { + update(dp[i + 1][j + 1], dp[i][j]); + if (j) update(dp[i + 1][j - 1], dp[i][j]); + } + } + + int cur = 0, cur_min = 0; + + for (auto &c: s) { + cur += (c == '(' ? 1 : -1); + cur_min = min(cur_min, cur); + } + + int ans = 0; + for (int i = 0; i <= n - m; i ++) { + for (int j = 0; j <= 2000; j ++) { + if (j + cur_min >= 0 && j + cur >= 0 && j + cur <= 2000) { + update(ans, 1ll * dp[i][j] * dp[n - m - i][j + cur] % mod); + } + } + } + + cout << ans; + + return 0; +} +``` \ No newline at end of file