题目链接:http://codeforces.com/problemset/problem/785/D
题意:给你一个只包含'('和')'的字符串,然后问他的子序列中有多少满足前一半是左括号,后一半是右括号。
分析:看到题就想到是组合数学,对于一个左括号,他能影响到的右括号就是他后边的,因此,你需要求前缀和和后缀和,来这样来求组合数。现在我们枚举左括号,当枚举到他时,代表他一定被选,前缀和为n,后缀和为m,然后在他前边选i=0到min(n,m)-1个,在他后边选前边数+1个。然后就是C(n-1,i)*C(m,i+1) ,也就是C(n-1,i)+C(m,m-i-1);求和即为C(n+m-1,m-1),然后用卢卡斯定理求即可,注意存n!还有求逆元。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 long long mod=1e9+7; 5 char s[200005]; 6 int a[200005]; 7 int b[200005]; 8 long long num[200005]; 9 long long qpow(long long x,long long n){ 10 if(n==0) return 1; 11 long long ans=1; 12 x=x%mod; 13 while(n!=0){ 14 if(n&1) { 15 ans=ans*x%mod; 16 } 17 x=x*x%mod; 18 n=n/2; 19 } 20 return ans; 21 } 22 long long Cn(long long n,long long m){ 23 if(n<m) return 0; 24 if(n==m) return 1; 25 if(m>n-m) m=n-m; 26 27 return (num[n]*qpow(num[n-m]*num[m],mod-2))%mod; 28 } 29 30 int main(){ 31 ios_base::sync_with_stdio(0); 32 cin.tie(0); 33 num[0]=1; 34 for(long long i=1;i<=200000;i++){ 35 num[i]=num[i-1]*i%mod; 36 } 37 cin>>s; 38 int d=strlen(s); 39 int num1=0,num2=0; 40 for(int i=0;i<d;i++){ 41 if(s[i]=='('){ 42 num1++; 43 } 44 a[i]=num1; 45 } 46 for(int i=d-1;i>=0;i--){ 47 if(s[i]==')'){ 48 num2++; 49 } 50 b[i]=num2; 51 } 52 long long result=0; 53 for(int i=0;i<d;i++){ 54 if(s[i]=='('){ 55 result=(result+Cn(a[i]+b[i]-1,b[i]-1))%mod; 56 //cout<<result<<endl; 57 } 58 } 59 cout<<result<<endl; 60 return 0; 61 }