Something Comforting
题目背景
![Something Comforting](https://mivik.gitee.io/image/nurture/something_comforting.png)
> Cause getting made you want more
>
> And hoping made you hurt more
>
> Someone tell me
>
> Something comforting
题目描述
Porter Robinson 花了五年的时间制作了 Something Comforting 这首歌,Mivik 花了五天时间造出了一道和括号序列相关的题。但 Mivik 并不开心,因为他发现他不会造数据了!
Mivik 需要随机生成一个 **合法** 的括号序列,于是 Mivik 想了一会,写出了下面的算法:
```cpp
#include <algorithm>
#include <string>
std::string generate(int n) { // 生成一个长度为 n * 2 的括号序列
const int len = n * 2;
bool arr[len]; // 0 代表左括号,1 代表右括号
for (int i = 0; i < n; ++i) arr[i] = 0;
for (int i = n; i < len; ++i) arr[i] = 1;
std::random_shuffle(arr, arr + len); // 随机打乱这个数组
for (int i = 0, j, sum = 0; i < len; ) {
sum += arr[i]? -1: 1;
if (sum < 0) { // 出现了不合法的位置
for (j = i + 1; j < len; ++j) {
sum += arr[j]? -1: 1;
if (sum == 0) break;
}
// 现在 i 是第一个不合法的位置,j 是 i 后面第一个合法的位置
// ( ( ) ) ) ) ( ( ( ) ( )
// i j
for (int k = i; k <= j; ++k)
arr[k] ^= 1; // 把这段区间全部反转
i = j + 1;
} else ++i;
}
std::string ret;
for (int i = 0; i < len; ++i)
ret += arr[i]? ')': '(';
return ret;
}
```
P.S. 为了给其它语言用户带来做题体验,[这里](https://www.luogu.com.cn/paste/wof8zjn8) 提供了多种语言对该算法的描述。
Mivik 十分开心,因为这个算法总能生成合法的括号序列。但不一会儿他就发现这个算法生成的括号序列并不均匀,也就是说,当 $n$ 固定时,所有合法的括号序列出现的概率并不均等。例如,Mivik 发现当 $n=3$ 时,`()()()` 被生成的概率要远大于 `((()))`。
现在 Mivik 给了你一个 $n$ 和一个长度为 $2n$ 的 **合法** 括号序列,假设 `std::random_shuffle` (对于其它语言来说,`shuffle`)能够均匀随机地打乱一个数组,他想问问你这个括号序列通过上文的算法被生成的概率是多少。由于 Mivik 不喜欢小数,你需要输出这个概率对 $998244353$ 取模的结果。如果你不知道如何将一个有理数对质数取模,可以参考 [有理数取模](https://www.luogu.com.cn/problem/P2613)。
输入输出格式
输入格式
第一行一个整数 $n$,意义同题面。
接下来输入一个长度为 $2n$ 的合法括号序列,意义同题面。
输出格式
输出一行一个整数,代表这个概率对 $998244353$ 取模的结果。
输入输出样例
输入样例 #1
1
()
输出样例 #1
1
输入样例 #2
3
()(())
输出样例 #2
598946612
说明
### 样例解释
样例一:$n$ 为 1 时,无论怎样都只可能会生成 `()` 这一种合法的括号序列,因此概率为 1。
### 数据范围
对于全部数据,有 $1\le n\le 5\cdot 10^5$,且输入的括号序列合法。
Subtask 1(20 pts):保证 $1\le n\le 5$。
Subtask 2(30 pts):保证 $1\le n\le 1000$。
Subtask 3(50 pts):无特殊限制。