BCPC Spring Training5 TE
主要知识点
题面
读入一个括号序列,问有多少个子序列满足完美括号匹配?
子序列指任意取。
完美括号匹配指的是左括号全在左边,右括号全在右边,并且匹配。
算法
首先的想法是我们扫描序列,对于每个左括号,我们将它强制为最内层左括号,计算左边右边选括号进行匹配的总数。
这样好办,从头到尾记录左括号前缀和l[],从后往前记录右括号后缀和r[],然后扫描每个左括号,加入答案。
而每个左括号的贡献就是
\[\sum_{i=0}^{min(l,r-1)}C_l^i*C_{r}^{i+1}
\]
通过范德蒙恒等式和预处理阶乘以及阶乘逆元可以O(1)处理。
\[\sum_{i=0}^{min(l,r-1)}C_l^i*C_{r}^{r-1-i}=C_{l+r}^{r-1}
\]
代码
#include <bits/stdc++.h>
using namespace std;
string s;
int l[200010], r[200010];
#define LL long long
const LL MOD = 1e9 + 7;
LL fac[200010], fac_inv[200010], inv[200010];
int main()
{
cin >> s;
int n = s.size();
fac[0] = 1;
for (int i = 1; i <= n; ++i)
fac[i] = fac[i - 1] * i % MOD;
inv[1] = 1;
for (int i = 2; i <= n; ++i)
inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
fac_inv[0] = 1;
for (int i = 1; i <= n; ++i)
fac_inv[i] = fac_inv[i - 1] * inv[i] % MOD;
int cnt = 0;
for (int i = 0; s[i]; ++i)
{
l[i] = cnt;
if (s[i] == '(')
++cnt;
}
cnt = 0;
for (int i = n - 1; i >= 0; --i)
{
r[i] = cnt;
if (s[i] == ')')
++cnt;
}
LL ans = 0;
for (int i = 0; r[i] >= 1; ++i)
{
if (s[i] == ')')
continue;
ans = (ans + fac[l[i] + r[i]] * fac_inv[r[i] - 1] % MOD * fac_inv[l[i] + 1]) % MOD;
}
cout << ans << endl;
return 0;
}