CF785D Anton and School - 2
题意:给定一个长度(le 2 imes 10e5)由'('和')'组成的字符串,问有多少个子串(可以不连续),前半部分是由('(')组成后半部分由(')')组成.
考虑枚举每一个可能的字串的中间,然后统计两边
先预处理每一个位置前面有多少个('('),后面有多少个(')')
然后枚举每一个('(')表示中间且必选
设当前位(包括)有(a)个('(')
则对此位置的答案即为(sum_{i=0}^{min(a-1,b-1)} C_{a-1}^i imes C_b^{i+1})
引理:范德蒙恒等式,(sum_{i=0}^k C_a^i imes C_b^{k-i}=C_{a+b}^k)
对上式进行化简即可得 (C_{a+b-1}^a)
预处理阶乘和阶乘逆元即可
Code:
#include <cstdio>
#include <cstring>
#define ll long long
const int N=200010;
const ll mod=1e9+7;
ll t[N],f1[N],f2[N],fac[N],inv[N],n,ans;
char c[N];
ll quick_pow(ll d,ll k)
{
ll f=1;
while(k)
{
if(k&1)
f=f*d%mod;
d=d*d%mod;
k>>=1;
}
return f;
}
ll C(ll a,ll b)
{
if(a>b) return 0;
if(inv[b-a]==-1) inv[b-a]=quick_pow(fac[b-a],mod-2);
if(inv[a]==-1) inv[a]=quick_pow(fac[a],mod-2);
return fac[b]*inv[b-a]%mod*inv[a]%mod;
}
int main()
{
memset(inv,-1,sizeof(inv));
scanf("%s",c);
n=strlen(c);
for(int i=0;i<n;i++)
t[i+1]=(c[i]==')');
for(int i=1;i<=n;i++)
f1[i]=f1[i-1]+!t[i];
for(int i=n;i;i--)
f2[i]=f2[i+1]+t[i];
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=fac[i-1]*i%mod;
for(int i=1;i<=n;i++)
if(!t[i])
{
ll a=f1[i],b=f2[i];
(ans+=C(a,a+b-1))%=mod;
}
printf("%lld
",ans);
return 0;
}
2018.7.19