标签:
BC # 32 1003
题意:定义了括号的合法排列方式,给出一个排列的前一段,问能组成多少种合法的排列。
这道题和鹏神研究卡特兰数的推导和在这题中的结论式的推导:
首先就是如何理解从题意演变到卡特兰数:
排列的总长度为 n ,左右括号各为 m = n / 2 个。当给定的排列方式完全合法的时候,剩下需要排列的左右括号的数量就已经确定了,而在排列的过程中,左括号要始终大于等于右括号的数量。设现在有 a 个左括号, b 个右括号,那么这个就可以当做从( a , b )点到 ( m , m )点且不越过直线 y = x 的种类数。将图像下移,则可认为是从( 0 , 0 )点到 ( p , q )点且不越过直线 y = x 的种类数。
求种类数则是用总的种类数减去非法种类数。总的种类数可用组合数学 C(p+q,p),而非法种类数则是通过图形移动,将图形向下移动一个单位,原本要求越过 y = x ,移动后只要从( 0 , - 1 )点到( p , q - 1 )点且经过 y = x 的路径都是非法的。而计算这个种类数则可以用( 0 , - 1 )与( - 1 , 0 )关于 y = x 对称得,只要从计算从( - 1 , 0 )点到( p , q - 1 )的种类数即可,因为这两点分别在 y = x 的上下,所以路径一定经过 y = x ,种类数为 C ( p + q ,p - 1 );
相减得到 (( q - p + 1 ) / ( q + 1 )) * C(p+q,p);
但事实上,直接这样计算是会超时的,因此可以将这个式子继续化成:
(( p + q )!*( q - p + 1 ))/((q + 1)!* p!)
在这个式子中大量用到阶乘就是为了可以直接在开始时预处理出阶乘来缩短时间。
而除法则用到了求逆元。
1 #include<stdio.h> 2 #include<string.h> 3 #define ll long long 4 const ll mod=1000000007; 5 char s[1000005]; 6 ll A[1000005]; 7 8 void fun(){ 9 A[0]=A[1]=1; 10 for(int i=2;i<=1000000;i++){ 11 A[i]=(A[i-1]*i)%mod; 12 } 13 } 14 /* 15 ll C(ll a,ll b){ 16 ll i,ans=1; 17 for(i=1;i<=b;i++){ 18 ans*=a-i+1; 19 ans/=i; 20 } 21 return ans; 22 } 23 */ 24 ll QuickPow(ll a,ll n){ 25 ll tmp=a,ans=1; 26 tmp %= mod; 27 while(n){ 28 if(n&1) ans=ans*tmp%mod; 29 tmp=tmp*tmp%mod; 30 n>>=1; 31 } 32 return ans; 33 } 34 35 int main(){ 36 int n; 37 fun(); 38 while(scanf("%d%s",&n,s)!=EOF){ 39 if(n%2)printf("0\n"); 40 else{ 41 int p=0,q=0,i,l=strlen(s); 42 for(i=0;i<l;i++){ 43 if(s[i]==‘(‘)p++; 44 else q++; 45 if(p<q){ 46 printf("0\n"); 47 break; 48 } 49 } 50 if(p<q) continue; 51 { 52 p=n/2-p; 53 q=n/2-q; 54 if(p<0||q<0){ 55 printf("0\n"); 56 continue; 57 } 58 ll r1=QuickPow(A[p],mod-2),r2=QuickPow(A[q+1],mod-2); 59 ll ans=A[p+q]; 60 // printf("%lld %lld %lld\n",ans,r1,r2); 61 ans=(ans*((q-p+1)%mod))%mod; 62 ans=(ans*r1)%mod; 63 ans=(ans*r2)%mod; 64 printf("%I64d\n",ans); 65 66 } 67 } 68 } 69 return 0; 70 }
标签:
原文地址:http://www.cnblogs.com/cenariusxz/p/4331271.html