组合数的素数算法(ACM基础教程1010)

题目描述:

  
  
组合数 TimeLimit: 1 Second MemoryLimit: 32 Megabyte Totalsubmit: 58 Accepted: 4 Description 从A + B个不同的物品中选择A个物品,共有多少种不同的选法。由于A,B会很大,所以结果对C取下余。 Input 首先输入T,代表共有T组测试数据。 每组数据包括三个数字,A,B,C; Output 输出只有一个数,如题目描述。 Sample Input 2 1 1 100 2 2 100 Sample Output 2 6

开始我以为一个简单的递归就可可以过,也就是C(n,m)=C(n-1,m)+C(n-1,m-1)组合数公式,但却tle;因为题目已经说明AB会很大,那么递归与暴力枚举是不可能AC的,那么该怎么做呢?:利用素数求法:

知识点:

这用到的是哥德巴赫猜想:热河一个数都可以有几个素数表示
1.任何一个实数都可以写成几个素数的和(1+1=2)
2.热河一个实数都可以写成几个素数的积(3!=3x2,6!=2^4*3^2*5^1)底数都是素数
3.a*b%c=(a%c)*(b%c)

这样我们就可以将C(n,m)分解为素数相乘的模式,如:C(6,3)=(2^4*3^2*5^1)/((3x2)*(3x2));

这时我们将素数的指数打表得

14    2     1
21    1     0
31    1     0

第一行减去的其余两行的 2   0   1那么结果就是2^2*3^0*5^1=20;

具体解题流程:

1.将素数打表,这方法有很多,可以用筛选法打表,也可以用其他打表方式

2.将N!分解为素数相乘的形式,然后将分解得到的指数写入数组表中,这提供一种方法代码:

1void factor ( int n, int * r )
2{
3        int i, j;
4        memset ( r, 0x00, PLEN * sizeof int ) );//数组初试化,注意含string.h
5        for ( i = 0; i < PLEN; ++i )
6        {
7            for ( j = n; j; r[i] += ( j /= p[i] ) );//数组p[i]为素数表,预先将素数打表完毕。(就是将0~n之间的素数按从小到大写入数组中,n=100000)
8        }
9}

然后将指数表相减求模;

如:C(A+B,A)=(A+B)!/(A!*B!)的到得指数表为AB[],A[],B[];

  AB[i]-=(A[i]+B[i]);

最后将AB[]表与素数表运算的结果(注意A*B%C=(A%C)*(B%C));

AC Code:(不理解,不会AC)

  
  
1 #include < stdio.h > 2 #include < string .h > 3 #include < math.h > 4 5   #define LEN 100000 6   #define PLEN 9592 7 8 void sieve ( char * f, int l ) { 9 int i, j, ilim; 10 11 memset ( f, 0x01 , l ); 12 f[ 0 ] = f[ 1 ] = 0 ; 13 ilim = ( int ) sqrt ( ( double ) l ); 14 15 for ( i = 2 ; i <= ilim; ++ i ) { 16 if ( ! f[i] ) continue ; 17 for ( j = i * i; j < l; j += i ) f[j] = 0 ; 18 } 19 } 20 21 char f[LEN]; 22 int p[PLEN]; 23 int u[PLEN], da[PLEN], db[PLEN]; 24 25 void factor ( int n, int * r ) 26 { 27 int i, j; 28 memset ( r, 0x00 , PLEN * sizeof ( int ) ); 29 for ( i = 0 ; i < PLEN; ++ i ) 30 { 31 for ( j = n; j; r[i] += ( j / = p[i] ) ); 32 } 33 } 34 35 int main ( ) { 36 int i, j, k; 37 int T, A, B, C; 38 39 sieve ( f, LEN ); 40 for ( i = 2 , j = 0 ; i < LEN; ++ i ) if ( f[i] ) p[j ++ ] = i; // p[i]初始化 41 for ( int i = 0 ;i < 20 ;i ++ )printf( " %d " ,p[i]); 42 for ( scanf ( " %d " , & T ); T -- ; ) { 43 scanf ( " %d%d%d " , & A, & B, & C ); 44 factor ( A , u ); 46 factor ( A, da ); 47 factor ( B, db ); 48 for ( i = 0 , j = 1 ; i < PLEN; ++ i ) { 49 k = u[i] - da[i] - db[i]; 50 while ( k -- ) j = j * p[i] % C; 51 } 52 printf ( " %d /n " , j ); 53 } 54 55 return 0 ; 56 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值