D. Anton and School - 2
题意
给定一个字符串 s ,只含有字符 ( 和 )。问 s 有多少个子序列(可以不连续) sub ,满足如下条件:
- sub 不为空串
- sub 为偶数长度
- sub 串的前半串均为 (
- sub 串的后半串均为 )
思路
官方题解是推导出一个容易求解的同等情况,比较容易想到的是对于每个左括号,求得前面的左括号个数以及后面的右括号个数,求组合数相乘累加.
直接复制了一份题解
代码
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b) for(int i=a;i<b;i++)
const int INF=0x3f3f3f3f;
const int maxn=2e5+50;
const int mod=1e9+7;
const double PI=acos(-1);
typedef long long ll;
typedef unsigned int ui;
using namespace std;
char buf[maxn];
int cl[maxn],cr[maxn];
const char LB='(',RB=')';
//逆元+阶乘打表直接求组合数
ll fac[maxn];
ll inv(ll a){
return(a==1?1:inv(mod%a)*(mod-mod/a)%mod);
}
void init(int Mod){
fac[0]=1;
for(int i=1;i<maxn;i++){
fac[i]=(fac[i-1]*i)%Mod;
}
}
ll C(int n,int m){
ll ret=fac[n];
ret=(ret*inv(fac[m])%mod)*inv(fac[n-m])%mod;
return ret;
}
int main()
{
#ifndef ONLINE_JUDGE
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
#endif
init(mod);
//int n,m;
//while(cin>>n>>m ) cout<<C(n,m)<<endl;
while(~scanf(" %s",buf+1))
{
int len=strlen(buf+1);
cl[0]=cr[0]=0;
for(int i=1;i<=len;i++) {
char c=buf[i];
if(c==LB) cl[i]=cl[i-1]+1;
else cl[i]=cl[i-1];
if(c==RB) cr[i]=cr[i-1]+1;
else cr[i]=cr[i-1];
}
ll ans=0;
for(int i=1;i<=len;i++){
char c=buf[i];
if(c==LB){
int tll=cl[i-1],tr=cr[len]-cr[i],tl=cl[i];
ans=(ans+C(tl+tr,min(tl,tr)))%mod;
ans=(ans-C(tll+tr,min(tll,tr))+mod)%mod;
//cout<<i<<" left "<<tl<<" "<<tll<<" right "<<tr<<endl;
//cout<<i<<" "<<ans<<endl;
}
}
cout<<ans<<endl;
}
return 0;
}