[2003 Ptz WC Day6] Little Brackets
思路
题意
给定序列长度 , 求「深度」为 的合法括号序列有多少个
其中「深度」定义为序列前缀中左括号数量与右括号数量之差的最大值
性质
对于一个正则括号串, 一定可以表示成唯一的形式:
其中 都是正则括号串
正则括号串的性质:
正则括号串可以通过递归方式定义:
- 空串是正则括号串。
- 如果 是正则括号串,则 也是正则括号串。
- 如果 和 是正则括号串,则 和的连接 也是正则括号串
证明
证明
假设我们有一个正则括号串 ,它可以表示为两种形式:
其中 、、 和 都是正则括号串,且 和 的长度相同。
我们需要证明 。
证明过程
1. 长度相同的含义
由于 和 的长度相同,且它们都是正则括号串,这意味着它们包含相同数量的左括号和右括号。设 和 的长度为 ,那么它们各自包含 个左括号和 个右括号。
2. 括号匹配的性质
在正则括号串中,每个左括号必须与一个右括号配对,且括号的嵌套是合法的。这意味着在任何前缀中,左括号的数量不能少于右括号的数量。
3. 唯一性表示
正则括号串的唯一性表示意味着,如果两个正则括号串的长度相同,且它们的左括号和右括号的数量相同,那么它们必须是相同的字符串。这是因为正则括号串的结构是由括号的配对和嵌套关系唯一确定的。
4. 归纳法证明
基础情况
当 时, 和 都是空串,显然 。
归纳假设
假设对于所有长度小于 的正则括号串,如果它们的长度相同,那么它们必须是相同的字符串。
归纳步骤
考虑长度为 的正则括号串 和 。由于 和 的长度相同,且它们都是正则括号串,它们的括号配对关系必须是一致的。因此, 和 的递归结构也必须是一样的。根据归纳假设, 和 的子结构也必须是相同的。因此,。
有了以上性质, 我们可以考虑类似区间 \(\rm{dp}\) 的做法
具体的, 我们可以把深度为 \(p\) 的 \(X\) 和深度为 \(q\) 的 \(Y\) 拼成深度为 \(\max (p + 1, q)\) 的正则括号串
令 \(f_{i, k}\) 表示对于长度为 \(2i\) 的括号串, 其中深度为 \(k\) 的方案数
考虑转移
初始化 \(f_{1, 1} = 1\)
去重
你发现光这样是不够的, 会算重
具体原因可以发现是出现了
, 其中 深度和 深度都为 , 其会被反复统计
我们考虑记录这种情况是否被统计过即可去重
代码
#include <bits/stdc++.h>
int n, m;
__int128 f[52][64]; //0, 1
template <typename _Tp>
inline void write(_Tp x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
return;
}
int main()
{
freopen("brackets.in", "r", stdin);
freopen("brackets.out", "w", stdout);
bool flag[50][61]; //2, 4
f[0][0] = f[1][1] = 1;
for (int i = 2; i <= 50; i++)
for (int k = 1; k <= i; k++) {
for (int j = 0; j < i; j++) {
for (int q = 0; q <= std::min(i - j - 1, k); q++) {
if (q == k) flag[j][i - j - 1] = true;
f[i][k] += f[j][k - 1] * f[i - j - 1][q];
}
for (int p = 0; p <= std::min(k - 1, i - j - 1); p++) {
if (p == k - 1 && flag[i - j - 1][j]) continue;
f[i][k] += f[j][k] * f[i - j - 1][p];
}
}
}
for (int cas = 1; ; cas++) {
scanf("%d %d", &n, &m);
if (n == 0 && m == 0) break;
printf("Case %d: ", cas); write(f[n][m]); printf("\n\n");
}
return 0;
}
总结
用于唯一分解正则括号串的性质
常见计数 $\rm{dp}$ 技巧: 去重
我们找到 会重的部分, 打上标记即可快速处理

浙公网安备 33010602011771号