Skip to content

Commit

Permalink
Editorial for CF629C
Browse files Browse the repository at this point in the history
  • Loading branch information
Yawn-Sean committed Jan 9, 2025
1 parent 604b63d commit f83f5c2
Showing 1 changed file with 97 additions and 2 deletions.
99 changes: 97 additions & 2 deletions daily_problems/2025/01/0109/solution/cf629c.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,107 @@

一个括号序列合法,等价于把左括号看成 $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]$ 的转移。

后缀部分的转移本质上是一样的,我们可以把后缀部分反转后,再把 $1$ 变成 $-1$ , $-1$ 变成 $1$ 。

我们可以枚举前缀的长度和数值和。则前缀加后缀的总长度等于 $n-m$ ,且整体和为 $0$ ,再加上前缀部分的和 $x$ 与中间部分的最小前缀和的和非负,即可完成前缀后缀两部分的选择。

时间复杂度为 $\mathcal{O}((n-m)^2)$ 。
时间复杂度为 $\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<vector<int>> dp(2001, vector<int>(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;
}
```

0 comments on commit f83f5c2

Please sign in to comment.