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);
}
/*
*/

浙公网安备 33010602011771号