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;
}
浙公网安备 33010602011771号