CF1924D Balanced Subsequences

题意:

给定 \(n,m,k\),求有多少个由 \(n\)(\(m\)) 组成的序列满足最长的合法括号子序列的长度恰为 \(2k\)

\(10^9+7\) 取模,\(n,m,k\leq 2000\)

分析:

先钦定 \(n \ge m\)。当 \(k > m\) 时,答案为 \(0\)

当给定一个括号序列的时候,如何求最长的合法括号子序列呢?记一个变量 \(f\),从左到右扫一遍,当进来一个 \((\) 时,\(f \leftarrow f+1\),当进来一个 \()\) 时,如果 \(f > 0\)\(f \leftarrow f-1\)。最终子序列长度即为 \(f\)\(1\) 的次数。

如何计数呢?设初始时在 \((0,0)\),每次横坐标加 \(1\),进来左括号时加 \(1\),进来右括号时减 \(1\),这描述的是整个序列的折线。由于有 \(m-k\) 个右括号会失配,那么这个折线所能到达的最低纵坐标恰好为 \(k-m\)

因此我们需要统计从 \((0,0)\) 开始走,每次可以往右上或右下走,满足最低纵坐标为 \(k-m\),走到 \((n+m,n-m)\) 的方案数。记 \(P(v)\) 表示最低纵坐标至多\(k-m\) 的方案数,那么答案就是 \(P(v)-P(v-1)\)。将第一次触碰到 \(y=v-m\) 后的路线反转,因此最终终点可以看作 \((n+m,2v-n-m)\)(与卡特兰数的一个推导相似),易得 \(P(v)=\binom{n+m}{v}\)

时间复杂度 \(O(n+m+T)\)

代码:

#include<bits/stdc++.h>
#define int long long
#define N 4010
#define mod 1000000007
using namespace std;

int T, n, m, k;

int Pow(int a, int n) {
	if(n == 0) return 1;
	if(n == 1) return a;
	int x = Pow(a, n / 2);
	if(n % 2 == 0) return x * x % mod;
	else return x * x % mod * a % mod;
}
int inv(int x) {
	return Pow(x, mod - 2);
}
int Inv[N], fac[N];
void init() {
	fac[0] = Inv[0] = 1;
	for(int i = 1; i <= 4000; i++) fac[i] = fac[i - 1] * i % mod;
	Inv[4000] = inv(fac[4000]);
	for(int i = 3999; i >= 1; i--) Inv[i] = Inv[i + 1] * (i + 1) % mod;
}
int C(int n, int m) {
	return fac[n] * Inv[m] % mod * Inv[n - m] % mod;
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	init();
	
	cin >> T;
	while(T--) {
		cin >> n >> m >> k;
		if(n < m) swap(n, m);
		if(k > m) cout << 0 << endl;
		else cout << (C(n + m, k) - C(n + m, k - 1) + mod) % mod << endl;
	}
	
	
	return 0;
} 
posted @ 2024-06-28 17:13  小超手123  阅读(44)  评论(0)    收藏  举报