loading

CF1924D Balanced Subsequences

题意简述

\(n\) 个左括号和 \(m\) 个右括号,求最长合法括号子序列长度为 \(2k\) 的括号序列的数量,对 \(10^9+7\) 取模。多组数据。

\(T\le 3\times10^3,n,m,k\le 2\times10^3\)

分析

可能需要的前置知识:

如何求一个字符串的最长合法括号子序列?

维护一个括号栈,若遇到左括号则直接压入栈,若遇到右括号则取出栈顶左括号匹配(此时最长合法括号子序列长度 +2),若没有,则该右括号失配。

下面进入正题。

首先,长度为 \(2k\) 的合法括号序列包含 \(k\) 个左括号和 \(k\) 个右括号。

首先判掉 \(k>n,k>m\) 的情况,那么将会剩下 \(n-k\) 个废物左括号和 \(m-k\) 个废物右括号。

首先肯定是废物右括号在前,废物左括号在后,否则这一对废物括号会扩充合法括号子序列的长度。

将括号序列抽象成折线,初始位置在 \((0,0)\),加入一个括号就让横坐标 +1,此时若该括号为左括号则让纵坐标 +1,否则让纵坐标 -1。也就是说,折线要么往右上方走,要么往右下方走。

而我们会有 \(m-k\) 个废物右括号会失配,所以最终在某一横坐标之下,纵坐标会到达 \(k-m\),因为当废物右括号失配时,根据前置知识,此时的括号栈中没有多余的左括号了,也就是说前面的括号肯定全部匹配完毕了。

而最终我们要达到坐标 \((n+m,n-m)\)

题意转化为求一条 \((0,0)\) 开始,\((n+m,n-m)\) 结束,路径上要到达并不能越过 \(y=k-m\) 的折线数量。

发现,到达并不能越过 这个限制实在是太紧了。考虑类似将 \(=k\) 转化为 \(\le k-\le k-1\),设 \(ans_k\) 为允许折线越过 \(y=k-m\) 的方案数,则原题答案转化为 \(ans_k-ans_{k-1}\)

考虑怎么求 \(ans_k\)。考虑折线第一次到达 \(y=k-m\) 的那个点,将那个点之后的折线按照 \(y=k-m\) 轴对称一下,原题又转化为从 \((0,0)\) 走到 \((n+m,2k-n-m)\) 的方案数,这是一个组合数,为 \(ans_k=\dbinom{n+m}{k}\)。(简单推式子可得)

所以最终答案为 \(\dbinom{n+m}{k}-\dbinom{n+m}{k-1}\)

代码仅展示关键部分。

点击查看代码
int n,m,k;
int C[maxn][maxn];
void init(int lim){
	C[0][0]=1;
	rep(i,1,lim){
		C[i][0]=1;
		rep(j,1,i)C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
	}
}
void solve_the_problem(){
	n=rd(),m=rd(),k=rd();
	if(k>m||k>n)return (void)puts("0");
	write((C[n+m][k]-C[n+m][k-1]+mod)%mod,10);
}
/*

*/

posted @ 2024-01-28 22:37  dcytrl  阅读(57)  评论(0)    收藏  举报