令 \(S\) 为题面的 \(S'\)。
首先考虑刻画出 \(\texttt{()}\) 对应的折线。
首先如果 \(S\) 本身合法,那么直接 DP 算一下就行了。
否则考虑不合法的情况,考虑反转 \((l, r]\) 合法的情况的判定。
令 \(s_i\) 为前 \(S_{1\sim i}\) 的前缀和的值。
那么有:
- \(s_r - s_l = \frac{s_n}{2}\),考虑到这样子在反转后会变为 \(s_r - s_l = -\frac{s_n}{2}\),相对差值就是 \(-s_n\),那么就能使反转后 \(s_n = 0\) 了。
- \(i\le l\),这部分不会改变,所以就是 \(s_i\ge 0\)。
- \(i > r\),那么 \(s_i\) 肯定都会减去 \(s_n\),所以就是 \(s_i\ge s_n\)。
- \(l < i\le r\),发现这个反转的过程相当于是对于 \((l, r]\) 这一段关于 \(y = s_{l - 1}\) 对称了,那么就是要求 \(2s_l - s_i\ge 0\),换一种方式就是 \(s_i\le 2s_l\)。
同时又因为 \(s_l = s_r - \frac{s_n}{2}\),有 \(s_i\le 2s_r - s_n\),两边同减 \(s_n\) 有 \((s_i - s_n)\le 2(s_r - s_n)\),这样的好处就是对于 \(i, r\) 只需要考虑其对于 \(s_n\) 的相对位置了。
记 \(mn = \min s_i\),考虑把不合法的情况分为两种:
- \([mn < s_0] + [mn < s_n] = 1\)。
- \([mn < s_0] + [mn < s_n] = 2\)。
先考虑第 \(1\) 种。
考虑如果 \(mn < s_n\),那么可以把 \(s\) 翻转并反转,相当于是对于这个折线对称过来了,那么这个时候就有 \(mn < s_0\) 了,所以就只需要考虑 \(mn < s_0\) 的情况即可。
考虑到有 \(s_i\le 2s_l, (s_i - s_n)\le 2(s_r - s_n)\)。
所以可以知道,对于前缀 \(s_i\ge 0\) 这一段,\(l\) 肯定会选取到 \(\max s_i\) 的位置。
同时因为有 \(s_n\le mn < s_0 < s_l\),肯定能找到 \(s_r = s_l + \frac{s_n}{2}\),对于 \(r\),也是贪心的选取最靠左的 \(r\),因为这样中间的 \(\max s_i\) 就能尽量小。
考虑对 \(l, r\) 进行 DP。
可以设 \(f_{i, j, k}\) 为对于 \(0\sim i\),保证 \(s_i\ge 0, s_i = j, \max s_i = k\) 的方案数。
同时设 \(g_{i, j, k}\) 为对于 \(i\sim n\),保证 \((s_i - s_n)\ge (s_n - s_n) = 0, s_i - s_n = j\),选的 \(s_r\) 为 \(s_n + k\)(\(k = n + 1\) 代表这部分没选)的方案数。
转移式子不是很想写啊,看代码即可。
对于统计贡献,可以枚举最靠前的 \(s_{i} = 0, s_{i + 1} = -1\) 的位置,同时枚举下 \(s_l, s_n\)。
那么就可以得出 \(s_r\) 了。
如果 \(s_r\ge 0\),那么显然可以在 \(l\sim i\) 的位置就能找到合法的 \(r\),贡献为 \(f_{i, 0, s_l}\times g_{i + 1, -1 - sn, n + 1}\)。
否则就需要在右边选出来了,\(f_{i, 0, s_l}\times g_{i + 1, -1 - sn, sr - sn}\)。
接下来考虑第 \(2\) 种。
那么与上面差不多的,只不过对于 \(r\) 也要求 \(s_r\sim s_n\) 都需要 \(\ge s_n\),且 \(l, r\) 之间存在 \(< s_0, s_n\) 的部分。
考虑令左边合法(前缀 \(s_i\ge 0\))的部分的最大值为 \(A\),右边合法(后缀 \(s_i\ge s_n\))的部分的最大值为 \(B\)。
首先 \(s_l = A\),考虑令 \(A + \frac{s_n}{2}\le B\),即肯定能找到 \(s_r\)。
否则考虑逆着考虑这个折线(也就是上面提到的反转并翻转 \(S\)),肯定有 \(A' + \frac{s_n'}{2}\le B'\),最后再减去 \(A + \frac{s_n}{2} = B\) 这部分的贡献,因为会算重。
中间 DP 基本一致,只需要在 \(g\) 加一维 \(0 / 1\) 表示是否存在 \(s_i < s_n\) 的情况即可。
最后统计贡献的方式也大致相同。
时间复杂度 \(\mathcal{O}(n^3)\)。
#include<bits/stdc++.h>
constexpr int mod = 1e9 + 7;
inline void add(int &x, int y) {
x += y, x >= mod && (x -= mod);
}
const int maxn = 3e2 + 10;
int n, ans;
char s[maxn];
namespace solve1 {
int f[maxn][maxn];
inline void solve() {
f[0][0] = 1;
for (int i = 0; i < n; i++)
for (int j = 0; j <= i; j++) {
if (s[i + 1] != ')') add(f[i + 1][j + 1], f[i][j]);
if (s[i + 1] != '(' && j) add(f[i + 1][j - 1], f[i][j]);
}
add(ans, f[n][0]);
}
}
namespace solve2 {
int f[maxn][maxn][maxn];
inline void solve() {
memset(f, 0, sizeof(f));
f[0][0][0] = 1;
for (int i = 0; i < n; i++)
for (int j = 0; j <= i; j++)
for (int k = j; k <= i; k++) {
if (s[i + 1] != ')')
add(f[i + 1][j + 1][std::max(j + 1, k)], f[i][j][k]);
if (s[i + 1] != '(' && j)
add(f[i + 1][j - 1][k], f[i][j][k]);
}
}
}
namespace solve3 {
int g[maxn][maxn][maxn];
inline void solve() {
memset(g, 0, sizeof(g));
g[n][0][0] = g[n][0][n + 1] = 1;
for (int i = n; i; i--)
for (int j = 0; j <= n - i; j++) {
if (s[i] != '(')
add(g[i - 1][j + 1][n + 1], g[i][j][n + 1]), add(g[i - 1][j + 1][j + 1], g[i][j][n + 1]);
if (s[i] != ')' && j)
add(g[i - 1][j - 1][n + 1], g[i][j][n + 1]), add(g[i - 1][j - 1][j - 1], g[i][j][n + 1]);
for (int k = 0; k <= n - i; k++) {
if (s[i] != '(' && j + 1 <= k * 2 && j + 1 != k)
add(g[i - 1][j + 1][k], g[i][j][k]);
if (s[i] != ')' && j && j - 1 <= k * 2 && j - 1 != k)
add(g[i - 1][j - 1][k], g[i][j][k]);
}
}
for (int l = 0; l < n; l++) {
if (s[l + 1] == '(')
continue;
for (int sl = 0; sl <= l; sl++)
for (int sn = -n; sn; sn += 2) {
int sr = sl + sn / 2;
add(ans, 1ll * solve2::f[l][0][sl] * g[l + 1][-1 - sn][sr >= 0 ? (n + 1) : (sr - sn)] % mod);
}
}
}
}
int g[maxn][maxn * 2][maxn][2];
namespace solve4 {
inline void solve() {
memset(g, 0, sizeof(g));
g[n][n][0][0] = g[n][n][n + 1][0] = 1;
for (int i = n; i; i--)
for (int j = n - (n - i); j <= n + (n - i); j++) {
if (s[i] != '(') {
add(g[i - 1][j + 1][n + 1][0], g[i][j][n + 1][0]);
j + 1 >= n && (add(g[i - 1][j + 1][j + 1 - n][0], g[i][j][n + 1][0]), 1);
}
if (s[i] != ')' && j - 1 >= n) {
add(g[i - 1][j - 1][n + 1][0], g[i][j][n + 1][0]);
add(g[i - 1][j - 1][j - 1 - n][0], g[i][j][n + 1][0]);
}
for (int k = 0; k <= n - i; k++) {
if (s[i] != '(' && j + 1 - n <= k * 2) {
j + 1 - n != k && (add(g[i - 1][j + 1][k][0], g[i][j][k][0]), 1);
add(g[i - 1][j + 1][k][1], g[i][j][k][1]);
}
if (s[i] != ')' && j - 1 - n <= k * 2) {
j - 1 - n != k && (add(g[i - 1][j - 1][k][j - 1 < n], g[i][j][k][0]), 1);
add(g[i - 1][j - 1][k][1], g[i][j][k][1]);
}
}
}
for (int l = 0; l < n; l++) {
if (s[l + 1] == '(')
continue;
for (int sl = 0; sl <= l; sl++)
for (int sn = -n; sn < n; sn += 2) {
int sr = sl + sn / 2;
sr - sn >= 0 && (add(ans, 1ll * solve2::f[l][0][sl] * g[l + 1][-1 - sn + n][sr - sn][1] % mod), 1);
}
}
}
}
namespace solve5 {
inline void solve() {
memset(g, 0, sizeof(g));
g[n][n][0][0] = 1;
for (int i = n; i; i--)
for (int j = n - (n - i); j <= n + (n - i); j++) {
for (int k = 0; k <= n - i; k++) {
if (s[i] != '(') {
add(g[i - 1][j + 1][std::max(k, j + 1 - n)][0], g[i][j][k][0]);
j + 1 - n <= k * 2 && (add(g[i - 1][j + 1][k][1], g[i][j][k][1]), 1);
}
if (s[i] != ')') {
add(g[i - 1][j - 1][k][j - 1 < n], g[i][j][k][0]);
j - 1 - n <= k * 2 && (add(g[i - 1][j - 1][k][1], g[i][j][k][1]), 1);
}
}
}
for (int l = 0; l < n; l++) {
if (s[l + 1] == '(')
continue;
for (int sl = 0; sl <= l; sl++)
for (int sn = -n; sn < n; sn += 2) {
int sr = sl + sn / 2;
sr - sn >= 0 && (add(ans, mod - 1ll * solve2::f[l][0][sl] * g[l + 1][-1 - sn + n][sr - sn][1] % mod), 1);
}
}
}
}
int main() {
scanf("%d%s", &n, s + 1);
if (n & 1)
return puts("0"), 0;
solve1::solve();
solve2::solve();
solve3::solve();
solve4::solve();
solve5::solve();
std::reverse(s + 1, s + n + 1);
for (int i = 1; i <= n; i++)
s[i] != 'x' && (s[i] ^= '(' ^ ')');
solve2::solve();
solve3::solve();
solve4::solve();
printf("%d\n", ans);
return 0;
}
标签:le,LibreOJ,int,mn,ge,JOISC,安全门,solve,sn
From: https://www.cnblogs.com/rizynvu/p/18218794