CF1924D Balanced Subsequences 题解

先判掉 \(k>\min(n,m)\) 的情况。

首先有个明显的计算最长合法括号子序列的贪心方法:初始一个值为 \(0\) 的计数器,从左到右枚举每个字符,遇到 ( 时将计数器加一,遇到 ) 时如果计数器不为 \(0\) 就减一然后将答案加一。

考虑绘制它的折线图。初始时纵坐标为 \(0\),从左到右枚举每个字符,遇到 ( 加一,遇到 ) 减一。考虑有多少个 ) 不会被匹配到,即在开头需要加入的最少 ( 的数量使得折线不会有在横轴下方的部分。于是题目中的限制就转化为了要使折线能到达的最低点纵坐标为 \(k-m\)

\(f(k)\) 表示能到达的最低点纵坐标为 \(k-m\) 的折线个数,那么答案就是 \(f(k)-f(k-1)\)。考虑将折线最后一次碰到直线 \(y=k-m\) 以后的部分上下翻转,那么就转化为了从 \((0,0)\) 走到 \((n+m,2k-m-n)\) 的路径方案数,组合数直接求即可。

参考代码:

#include<bits/stdc++.h>
#define ll long long
#define mxn 4003
#define md 1000000007
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
int t,n,m,k;
ll fac[mxn],ifac[mxn];
ll power(ll x,int y){
	ll ans=1;
	for(;y;y>>=1){
		if(y&1)ans=ans*x%md;
		x=x*x%md;
	}
	return ans;
}
inline ll C(int n,int m){
	if(n<m||m<0)return 0;
	return fac[n]*ifac[m]%md*ifac[n-m]%md;
}
int f(int k){
	return C(n+m,(n+m-(2*k-m-n))>>1); 
}
void init(int n){
	fac[0]=1;
	rep(i,1,n)fac[i]=fac[i-1]*i%md;
	ifac[n]=power(fac[n],md-2);
	drep(i,n,1)ifac[i-1]=ifac[i]*i%md; 
}
signed main(){
	init(4e3);
	scanf("%d",&t);
	while(t--){
		scanf("%d%d%d",&n,&m,&k);
		if(k>min(n,m)){
			puts("0");
			continue;
		}
		cout<<(f(k)-f(k-1)+md)%md<<'\n'; 
	}
	return 0;
}
posted @   zifanwang  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示