CF785D Anton and School - 2

Solution

这个题经过一番思考,发现前面一位和后面一位之间是不会相互影响的,也就是各自的贡献是独立算的。

那我们就直接通过一些排列组合的方式来求出答案即可。

刚刚我提到了贡献,所以我们可以对这个括号序列的每一位进行计算,最后相加。然后发现因为他们是成对匹配的,当你算左括号的时候,右括号已经被算了一遍,算右括号的时候,左括号被算了一次,会重复计算。那么就只考虑左括号就行了。

记录当前位左边有多少左括号(包括自己),右边有多少右括号,设有 \(a\) 个左括号, \(b\) 个右括号,那么满足条件的子串会多 \(C_{a-1}^0C_b^1+C_{a-1}^1C_b^2+\cdots+C_{a-1}^xC_{b}^{x+1}+\cdots=\sum\limits_{i=0}^{\min(a-1,b-1)}C_{a-1}^xC_b^{x+1}\)

然后有个神奇的东西叫范德蒙德卷积,它是长这个样子的: \(\sum\limits_{i=0}^kC_n^iC_m^{k-i}=C_{n+m}^k\)

原题中的式子我们也可以类似的转化: \(\sum\limits_{i=0}^{\min(a-1,b-1)}C_{a-1}^xC_b^{x+1}=\sum\limits_{i=0}^{\min(a-1,b-1)}C_{a-1}^{a-x-1}C_b^{x+1}=C_{a+b-1}^a\)

呜呼,那么我们只要预处理出每一位的左括号,右括号和组合数就好了。

时间复杂度: \(O(len_s)\)

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define ll long long

using namespace std;
const int N=200010,mod=1e9+7;
char s[N];
int l[N],r[N];
ll ans,fac[N],inv[N];

ll fpow(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}

inline void init(){
    fac[0]=1;
    for(int i=1;i<=200000;i++) fac[i]=fac[i-1]*i%mod;
    inv[200000]=fpow(fac[200000],mod-2);
    for(int i=200000;i>=1;i--) inv[i-1]=inv[i]*i%mod;
}

inline ll C(ll n,ll m){
    if(n<m) return 0;
    return fac[n]*inv[n-m]%mod*inv[m]%mod;
}

int main(){
    init();
    scanf("%s",s+1);
    int len=strlen(s+1);
    for(int i=1;i<=len;i++)
        l[i]=(s[i]=='('?l[i-1]+1:l[i-1]);
    for(int i=len;i>=1;i--)
        r[i]=(s[i]==')'?r[i+1]+1:r[i+1]);
    for(int i=1;i<=len;i++)
        if(s[i]=='(') ans=(ans+C(l[i]+r[i]-1,l[i]))%mod;
    printf("%lld\n",ans);
    return 0;
}
posted @ 2020-10-14 21:33  jasony_sam  阅读(75)  评论(0编辑  收藏  举报