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;
}
posted @ 2022-03-08 17:47  cacu  阅读(54)  评论(0)    收藏  举报