CodeForces 785D Anton and School - 2 组合数学

题意:

定义一种合法的括号序列为:长度为正偶数,前半部分括号为左括号,后半部分括号为右括号
给出一个括号序列\(s\),求其合法子序列的个数

分析:

枚举合法子序列中最右的左括号\(s_i\),设\(s_i\)左边(包括\(s_i\))有\(L\)个左括号,\(s_i\)右边有\(R\)个右括号
这样的合法子序列的个数为\(\sum_{i=1}^{L}C_{L-1}^{i-1} \times C_R^i\)

考虑这样一个式子的化简:
\(\sum_{i=0}^n C_n^i \times C_m^i = \sum_{i=0}^n C_n^i \times C_m^{m-i}\)
右边式子的含义就是:有\(n+m\)个小球分为两部分,第一部分有\(n\)个,第二部分有\(m\)个,先从第一部分中取出\(i\)个,再从第二部分中取出\(m-i\)个,这就相当于直接从\(n+m\)个中取\(m\)
所以有\(\sum_{i=0}^n C_n^i \times C_m^i = \sum_{i=0}^n C_n^i \times C_m^{m-i} = C_{n+m}^m\)成立

回到题目中来,就是
\[\sum_{i=1}^LC_{L-1}^{i-1} \times C_R^i=\sum_{i=0}^{L-1}C_{L-1}^i \times C_R^{i+1}=\sum_{i=0}^{L-1}C_{L-1}^i \times C_R^{R-i-1}=C_{L+R-1}^{R-1}=C_{L+R-1}^L\]

其中对于每个左括号的\(L\)\(R\)可以通过递推求得

#include <cstdio>
#include <cstring>
typedef long long LL;

const LL MOD = 1000000007LL;
const int maxn = 200000 + 10;

void add(LL& a, LL b) { a += b; if(a >= MOD) a -= MOD; }

int n;
char s[maxn];
int l[maxn], r[maxn];

LL fac[maxn * 2], inv[maxn * 2];

LL pow_mod(LL a, int p) {
    LL ans = 1;
    while(p) {
        if(p & 1) ans = ans * a % MOD;
        a = a * a % MOD;
        p >>= 1;
    }
    return ans;
}

LL C(int n, int k) {
    return (fac[n] * inv[k] % MOD) * inv[n - k] % MOD;
}

int main()
{
    fac[0] = 1;
    for(int i = 1; i < maxn * 2; i++) fac[i] = fac[i - 1] * i % MOD;
    inv[maxn * 2 - 1] = pow_mod(fac[maxn * 2 - 1], MOD - 2);
    for(int i = (maxn - 1) * 2; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % MOD;

    scanf("%s", s + 1);
    n = strlen(s + 1);
    for(int i = 1; i <= n; i++) l[i] = l[i - 1] + (s[i] == '(');
    for(int i = n; i > 0; i--) r[i] = r[i + 1] + (s[i] == ')');

    LL ans = 0;
    for(int i = 1; i <= n; i++) if(s[i] == '(') {
        add(ans, C((((l[i] + r[i]) % MOD) + MOD - 1) % MOD, l[i]));
    }

    printf("%lld\n", ans);

    return 0;
}
posted @ 2017-03-18 00:20 AOQNRMGYXLMV 阅读(...) 评论(...) 编辑 收藏