HDU 3398 String 组合数学+N!质因数分解 卡特兰数

来源:互联网 发布:ipad淘宝发布宝贝 编辑:程序博客网 时间:2024/04/28 03:02

String

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2305    Accepted Submission(s): 668


Problem Description
Recently, lxhgww received a task : to generate strings contain '0's and '1's only, in which '0' appears exactly m times, '1' appears exactly n times. Also, any prefix string of it must satisfy the situation that the number of 1's can not be smaller than the number of 0's . But he can't calculate the number of satisfied strings. Can you help him?
 

Input
T(T<=100) in the first line is the case number.
Each case contains two numbers n and m( 1 <= m <= n <= 1000000 ).
 

Output
Output the number of satisfied strings % 20100501.
 

Sample Input
12 2
 

Sample Output
2
 
/*HDOJ 3398 组合数学+N!质因数分解汗!不会。卡特兰数 1大于0的个数 ①:我们设初始在坐标系的原点(0,0),从字符串第一位开始,碰到一个1就向上走,碰到一个0就向右走,那么由n个1、m个0组成的字符串最后必定走到(n,m)点,即满足由n个1、m个0组成的字符串的个数为C(n+m,n)=C(n+m,m) (满足n+m长度内n个长度走1或者m个长度走0)。②:对于任意前缀中1的个数不少于0的个数的字符串的个数这个条件,可以看成是坐标系中,从(0,0)点走到(m, n)点,并且跟y=x-1这条直线不相交的方案数。又因为(0,0)点关于直线y=x-1的对称点是(1,-1),而从(1,-1)点走到(m, n)点的所有方案一定都会与直线y=x-1相交,对于这些方案,将从(1,-1)点到与y=x-1的第一个交点之间的路径关于y=x-1对称翻转过去,就可以得到所有不满足题意的从(0,0)点走到(m, n)点的方案,C(n+m,m-1)=C(n+m,n+1) 为什么n+1 于是最终答案就是C(n+m, n)-C(n+m,n+1)。 接下来就是求上式子模20100501的值,因为n、m很大,所以我们利用质因数分解来分解阶乘,然后分子分母抵消指数最后求指数幂的和即可,但是这样做还是会TLE。而用对于阶乘,有很多非常好的性质可以利用(不利用就TLE。。。),下面介绍N!的质因数分解,也就是求N!中x的幂,首先因为N! = 1*2*3*……*N,所以N!中x的幂就是各个数质因数分解后x的幂之和,考虑含1,2,……,N中含x^1的共有x^1,2*x^1,……,y*x^1共y个,其中y=floor(N/x^1),而含x^2的共有x^2,2*x^2,……,y*x^2共y个,其中y=floor(N/x^2)=floor((N/x)/x),所以可以利用递归来计算N!中x的幂C(n+m, n)-C(n+m,n+1)=(n+m)!/(n+m-1)!n!-(n+m)!/(n+m-1)!(n+1)!=(n+1-m)(n+m)!/(n+1)!m!power定理分解素数,对于每个素数分别算幂,取模相乘即可。*/#include<iostream>#include<stdio.h>#include<algorithm> using namespace std;#define N 1000001typedef __int64 LL;int f[N*2+1],prime[N*2+1],k;/*8!/28/2=4 4/2=2 2/2=1第1轮 2 4 6 8 含有2的 4第2轮 1 2 3 4 含有2的 2第3轮 1 2  含有2的 1数字:2 4 6 8  1 2 1 3=7*/int cal(int p,int n)//计算n!是素数p的幂次 {int ans=0;while(n){n/=p;ans+=n;}return ans;}  void calPrime(){      memset(f,0,sizeof(f)); k=0;      for(int i=2;i<=N*2;i++){          if(f[i])continue;prime[k++]=i;         for(int j=i*2;j<=N*2;j+=i)f[j]=1;      } }   int main(){      int t,n,m,i; calPrime();      scanf("%d",&t);      while(t--){          scanf("%d%d",&n,&m);                 LL ans=1;int nm=n-m+1;          for(i=0;i<k&&prime[i]<=n+m;i++){              int cnt=0;              while(nm%prime[i]==0)//计算n-m+1的素数的幂 {nm/=prime[i];cnt++; } //计算整个公式的素数的幂            int ipow=cnt+cal(prime[i],n+m)-cal(prime[i],n+1)-cal(prime[i],m);  for(int j=1;j<=ipow;j++){                  ans=(ans*prime[i])%20100501;              }          }          printf("%I64d\n",ans);      }      return 0;  }  




0 0
原创粉丝点击