怎么你也是神仙题。
首先如何对于一个括号序列求它的最长合法括号子序列?
考虑把括号序列表示成折线形式,即 ( 设为 1,) 设为 −1。
那么选出的一定是形如这样的合法括号子序列:
即起点 - 最小值和终点 - 最小值这两部分不能选。
然后考虑贪心,由于 ( 的字典序小于 ),所以肯定尽量选择 () 这样的括号对放在前面。
因此不断贪心,直到这次不选多点后面就没得选了为止。此时后面一定恰好组成一个合法括号序列,方案唯一。
选择的括号序列大概长这样:()()()…(()()())…,即一旦当前选了不够,后面尽量长地选择,能匹配就尽量匹配。
如何计数?
先分讨一种比较容易的情况:S=()()()…。
设下标从 1 开始,那么 1 前不能放左括号,2 前不能放右括号,3 前不能放左括号……最后面任意放,不会影响到前面的最优括号序列。
总之就是对于前面的位置,放置方案唯一,因此只需要插板法就可以求出组合数;对于最后面就是二的次幂。
考虑枚举最后面的长度 i,那么后面方案数 2i,前面方案数是 n−i−k 个括号扔进 k 个位置的方案,即 Ck−1n−i−1。组合计数。(什么远古神帖)
然后考虑其余情况,设 S 前缀最长的 () 有 k 个。
根据贪心,前面的 () 必须都选。所以相邻两个 () 之间不能再出现 (),因此在折线上只能先降再升,即先 ) 再 ((下图蓝色段)。
(当然第一个括号之前也可以先降再升,忘记画了)
在这之后,就是贪心尽量长地选。这可以怎么刻画?
对于最低点之前,以前缀最小值划分“阶段”;之后以后缀最大值划分。
贪心选相当于不选下图红色部分,即选择剩余的若干个合法括号序列:
那这里要怎么插入括号?其实插入的括号就是上图的红色部分,即插入的所有括号放在一起,只能先降再升。而且只能插在左括号等于右括号位置的右边。
考虑对上面的做法计数。
对于前面一半,设上面的极长前缀 () 有 l 对,枚举若干个 V 字的总长度 i(前面图的蓝色段)。
那么相当于 i 长度被分为 2(l+1) 段,即有 l+1 个空位,每个空位分为上升和下降。
方案数为:Cii+2(l+1)−1。
对于后面一半,令 L=n−k,则大“V”字的总长度为 L−i=n−k−i。
由于下降和上升的“分隔点”可以随意确定,所以方案数为 (L−i+1)。
然后再乘上把“V”字放到中间段的方案数。设原序列有 c 段,则方案数为 Cc−1L−i+c−1。
于是这题就这么并不愉快地解决了。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e7 + 15, mod = 998244353;
int qmi(int a, int k) {
int res = 1;
while (k) {
if (k & 1) res = res * 1ll * a % mod;
a = a * 1ll * a % mod, k >>= 1;
}
return res;
}
int n, k;
int fac[N], inv[N];
char s[N];
void init() {
fac[0] = 1; for (int i = 1; i <= 10000000; i++) fac[i] = fac[i - 1] * 1ll * i % mod;
inv[10000000] = qmi(fac[10000000], mod - 2);
for (int i = 9999999; i >= 0; i--) inv[i] = inv[i + 1] * 1ll * (i + 1) % mod;
}
inline int C(int n, int m) { return fac[n] * 1ll * inv[m] % mod * 1ll * inv[n - m] % mod; }
long long ans = 0;
int main() {
init();
scanf("%d%d\n %s", &n, &k, s + 1);
int l = 0;
for (int i = 1; i <= k; i += 2) {
if (s[i] == '(' && s[i + 1] == ')') l++;
else break;
}
if ((l << 1) == k) { //全是括号对
for (int i = 0; i <= n - k; i++) (ans += qmi(2, i) * 1ll * C(n - i - 1, k - 1) % mod) %= mod;
return printf("%lld\n", ans), 0;
}
int L = n - k, c = 0, sum = 0;
for (int i = 1; i <= k; i++) {
if (s[i] == '(') sum++; else sum--;
if (i > l * 2) c += (sum == 0);
}
for (int i = 0; i <= L; i++) (ans += C(i + 2 * (l + 1) - 1, i) * 1ll * (L - i + 1) % mod * 1ll * C(L - i + c - 1, c - 1) % mod) %= mod;
printf("%lld\n", ans % mod);
return 0;
}