题解:CF2063F1 Counting Is Not Fun (Easy Version)

所有方案数均基于卡特兰数,不妨设 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值