所有方案数均基于卡特兰数,不妨设 F(x)=(2xx)x+1F(x) = \dfrac{\tbinom{2x}{x}}{x + 1}F(x)=x+1(x2x) 来表示卡特兰数。
初始时显然答案为 F(n)F(n)F(n)。当有括号加入时,未被添加括号的位置的贡献不会超过最外层已匹配的括号。举个例子,当 ??????→(??)???????? \to \red{(}??\red{)}????????→(??)?? 时,答案就变化为 F(3)→F(1)×F(1)F(3) \to F(1) \times F (1)F(3)→F(1)×F(1)。
因此可以先将这 nnn 对括号进行编号,然后借助栈,当遇到未被添加括号的位置时,将贡献加在栈顶的括号对应的编号上,最后根据乘法原理求解即可。
时间复杂度 O(n2)O(n^2)O(n2),代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <stack>
#define init(x) memset (x,0,sizeof (x))
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
using namespace std;
const int MAX = 5005;
const int MOD = 998244353;
inline int read ();
int t,n,p[MAX << 1],l[MAX],r[MAX],cnt[MAX];ll ans,f[MAX << 1],inv[MAX << 1],inv2[MAX << 1];
stack <int> s;
ll qpow (ll x,ll y)
{
ll res = 1;
while (y)
{
if (y & 1) res = res * x % MOD;
x = x * x % MOD;
y >>= 1;
}
return res;
}
ll catalan (int x) {return f[2 * x] * inv[x] % MOD * inv[x] % MOD * inv2[x + 1] % MOD;}
int main ()
{
//freopen (".in","r",stdin);
//freopen (".out","w",stdout);
f[0] = inv[0] = 1;
for (int i = 1;i <= 10000;++i) f[i] = f[i - 1] * i % MOD,inv2[i] = qpow (i,MOD - 2);
inv[10000] = qpow (f[10000],MOD - 2);
for (int i = 9999;i;--i) inv[i] = inv[i + 1] * (i + 1) % MOD;
t = read ();
while (t--)
{
n = read ();
for (int i = 1;i <= n;++i) l[i] = read (),r[i] = read ();
for (int i = 1;i <= 2 * n;++i) p[i] = 0;
for (int o = 0;o <= n;++o)
{
for (int i = 0;i <= n;++i) cnt[i] = 0;
p[l[o]] = o;p[r[o]] = -o;ans = 1;//新添加的括号进行标记
for (int i = 1;i <= 2 * n;++i)
{
if (!p[i])//未被添加的位置
{
if (s.size ()) ++cnt[s.top ()];//栈非空,算栈顶的贡献
else ++cnt[0];//否则算至最外层贡献之中
}
else if (p[i] < 0) s.pop ();//已经匹配,弹出即可
else s.push (p[i]);
}
for (int i = 0;i <= n;++i) ans = ans * catalan (cnt[i] / 2) % MOD;//注意左右括号各贡献一次,因此需要除以 2
printf ("%lld ",ans);
}
puts ("");
}
return 0;
}
inline int read ()
{
int s = 0;int f = 1;
char ch = getchar ();
while ((ch < '0' || ch > '9') && ch != EOF)
{
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9')
{
s = s * 10 + ch - '0';
ch = getchar ();
}
return s * f;
}
1199

被折叠的 条评论
为什么被折叠?



