题目链接:http://class.51nod.com/Challenge/Problem.html#problemId=2989
一、题目描述
组合数C(m,n),表示从M个数中选择N个,有多少种不同的方法。
组合数的计算公式如下:
给出m,n输出C(m,n)最后9位数,去掉前缀的0。
输入
一行,两个正整数N和M(N<=1000000,M<=1000000)。
输出
一行,输出C(m,n)最后9位数,去掉前缀的0。
样例输入
6 3
样例输出
20
二、思路描述
求1个数的最后9位且不考虑前缀0,相当于求这个数%1000000000的结果。
可以将1-n的每一个数都分解为素数幂的乘积,再统计每个素数的幂之和。
例如:
6!=1×2×3×4×5×6 = 2×3×22×5×2×3 = 24×32×5
3!=2×3
我们变成这个样子后就可以进行除法(幂为减法):
同底数幂相除,底数不变,指数相减:(同样是以2为底数的)
am ÷ an = a(m-n)
例如:a5 ÷ a2 = a(5-2) = a3
并不需要逐个分解1-n的每一个数,只需要求解出小于n的所有质数,然后套用求解阶乘后面0的方法(https://www.cnblogs.com/elisa02/p/12811208.html),求每个质数的幂次。
有了每个质因子的幂次后,再套用快速幂来求解(https://www.cnblogs.com/elisa02/p/12793231.html),这样复杂度降为O(nloglogn)。可以处理1e6范围的数据了。
三、代码
#include<cstdio> #include<iostream> using namespace std; long long mod = 1000000000; long long count(long long n, long long p){//在1~n的范围内含有p质因子的幂次之和是多少 long long ans = 0; for(long long i = p;i <= n;i *= p){ ans += n/i; } return ans; } long long p_mod(long long a, long long b, long long m){//a的b次方%m long long ans = 1; a = a % m; while(b > 0){ if(b & 1){ ans = (ans * a) % m; } a = (a*a) % m;//每一个位置都要乘一次平方 b >>= 1;//判断下一个位置了 } return ans;//返回累积的结果 } int main(){ long long m, n, ans=1, cnt; bool primes[1000010]={0}; cin >> m >> n; if(m < n){ cout << '0' << endl; return 0; } for(int i = 2;i <= m;i++){ if(!primes[i]){//如果primes[i]不为质数 cnt = count(m, i) - count(n, i) - count(m-n, i); ans = (ans * p_mod(i, cnt, mod)) % mod; for(long long j = (long long)i*i;j <= m;j += i){//如果primes[i]不为质数 //说明他的倍数不是质数 primes[j] = true; } } } cout << ans << endl; return 0; }