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;
}