题解:
我们定义第i位如果是 ( 则第 i 位必选 后面的 ( 全不要 前面的做全排列 这样就保证没有重复的
如果 i 前面有t1个( i 后面有t2个 ) 那么方案数就是
C(t1,0)*C(t2,1)+C(t1,1)*C(t2,2)+...+C(t1,min(t1,t2-1))*C(t2,min(t1,t2-1)+1)
我们有这个公式
假设n<m
C(n,0)*C(m,0)+C(n,1)*C(m,1)+...+C(n,n)*C(m,n)=C(n+m,m)
变换下
C(n,0)*C(m,m)+C(n,1)*C(m,m-1)+...+C(n,n)*C(m,m-n)=C(n+m,m)
这个数学意义就是 从一个袋子中有n个球和一个袋子有m个球的两个袋子中取出m个球的方案
那么第一个式子就是 从两个袋子中取出m-1个球的方案
所以
C(t1,0)*C(t2,1)+C(t1,1)*C(t2,2)+...+C(t1,min(t1,t2-1))*C(t2,min(t1,t2-1)+1)=C(t1+t2,t2-1)
ps:我的代码中t1是包含当前位的 所以求组合数时候要减去1
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
char s[200005];
ll a[200005],b[200005],fac[400005];
ll ans=0;
ll quick(ll a,ll k){
ll ans=1;
while(k){
if(k&1)ans=ans*a%mod;
a=a*a%mod;
k/=2;
}
return ans;
}
ll C(ll n,ll m){
return fac[n]*quick(fac[m],mod-2)%mod*quick(fac[n-m],mod-2)%mod;
}
int main(){
scanf("%s",s+1);
ll len=strlen(s+1);
ll i,j;
fac[0]=1;
for(i=1;i<=400000;i++)fac[i]=fac[i-1]*i%mod;
for(i=1;i<=len;i++){
if(s[i]=='(')a[i]=a[i-1]+1;
else a[i]=a[i-1];
}
for(i=len;i>=1;i--){
if(s[i]==')')b[i]=b[i+1]+1;
else b[i]=b[i+1];
}
for(i=1;i<=len;i++){
if(s[i]!='(')continue;
ll t1=a[i],t2=b[i+1];
if(t1==0)continue;
else if(t2==0)break;
if(t1==1){
ans+=C(t2,1);
}
else if(t2==1){
ans+=1;
}
else{
ans+=C(t1-1+t2,t2-1);
ans=ans%mod;
}
}
cout<<ans<<endl;
return 0;
}