一中OJ | #3167 排列组合数计算[2]
时限 1000MS/Case 内存 64MB/Case
题目描述
给出 m 个元素的集合,从其中选择 n 个元素的排列数为A(n,m)、组合数为C(n,m)。请你用递推算法计算这两个数。
输入格式
两个正整数 m 和 n 。
输出格式
第一行输出A(n,m);
第二行输出C(n,m);
这两个数可能很大,请把他们的结果模100000007后输出。
样例输入
5 2
样例输出
20
10
数据范围
1<=n<=m<=10^6
----------------------------------------------------------
题目分析
计算组合数的办法列如下:
1.组合数公式
最简单,对于m,n在20以内好使,在这里就不提了
2.组合数递推式(模拟杨辉三角形)
C(n,m)=C(n,n-m)=C(n-1,m-1)+C(n-1,m)
用递归是撑不了多久的,填表稍微好些,大概能算到10^3左右;优化后可以到10^4级别
10^4的递推代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#define hashsize 1000003
#define inf 0x7f7f7f7f
#define mo 100000007
using namespace std;
int n,m;
long long arrangement=1;
long long combination[500005]={0,1,2,1};
int main()
{
scanf("%d%d",&n,&m);
for(int i=n;i>=n-m+1;i--) arrangement=arrangement*i%mo;
if(m>n-m) m=n-m;
cout<<arrangement<<endl;
for(int i=3;i<=n;i++)
{
for(int j=min(m+1,i+1);j>=max(1,m+1-n+i);j--)
{
combination[j]=(combination[j]+combination[j-1])%mo;
}
}
cout<<combination[m+1];
return 0;
}
3.Lucas定理 && 4.乘法逆元
这个在其他地方有更详细的介绍,我也说不清楚【摊手】
传送门:
[组合数]求组合数的几种方法总结 - CSDN Blog @ 穆林幕
5.[重头戏]整数唯一分解定理(分解质因数)
任何大于 1 的自然数 n,都可唯一分解成有限个质因数乘积的形式:
然后我们已知组合数公式为
因为c(m,n)一定是整数
又因为m>n>(m-n),m!的质因子所有一定<m(因为m可分解成<=m的质因子们,那么m-1可以分更小,m-2...可以分更更小),所以可以先算出m以内的所有质数
然后获得m!,n!,(m-n)!的唯一质因数分解式,再将m!/(n!(m-n)!)中的除法当做质因数分解式指数的减法,最后算
(其中p<=m且p为质数,e[i]为做完指数减法后的指数)即可
----------------------------------------------------------
附:在O(n)内求出n!的唯一分解的方法
设P为质数,n∈正整数,则质数P在N!的质因数分解式中的幂次为{N/P^1+N/P^2+N/P^3+...+N/P^X | P^X <= N}
证明:假设N=10,P=2(此处'/'为整除)
◆1*2*3*4*5*6*7*8*9*10 中,含因子 2 的数为:2,4,6,8,10,即 10!中至少含有 10/2^1 个因子 2
将整个式子/2
◆1*1*3*2*5*3*7*4*9*5 中,仍然含有因子 2 的数为:2(原4/2),4(原8/2),即 10!中还含有 10/2^2 个因子 2
再将整个式子/2
◆1*1*3*1*5*3*7*2*9*5 中,仍然含有因子 2 的数为:2(原8/4),即 10!中仍然含有 10/2^3 个因子 2
其他数字仍可类比证明
----------------------------------------------------------
代码
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#define hashsize 1000003
#define inf 0x7f7f7f7f
#define mo 100000007
using namespace std;
bool isp[1000005];
vector<int>p;
int e[1000005];
long long arrangement=1;
int getzys(int n,int pool)
{
for(int k=0;k<p.size() && p[k]<=n;k++)
{
long long pp=p[k];
if(pool==1)
while(pp<=n) e[k]=e[k]+n/pp,pp=pp*p[k];
else
while(pp<=n) e[k]=e[k]-n/pp,pp=pp*p[k];
}
}
int main()
{
int m,n;
long long ans=1;
memset(isp,true,sizeof(isp));
isp[0]=isp[1]=false;
for(int i=2;i<=1000005;i++)
if(isp[i])
{
p.push_back(i);
for(int k=2;k*i<=1000005;k++) isp[k*i]=false;
}
scanf("%d%d",&n,&m);
for(int i=n;i>=n-m+1;i--) arrangement=arrangement*i%mo;
cout<<arrangement<<endl;
memset(e,0,sizeof(e));
getzys(n,1);
getzys(m,-1);
getzys(n-m,-1);
for(int k=0;p[k]<=n;k++)
{
long long t=1;
for(int i=1;i<=e[k];i++) t=(t*p[k])%mo;
ans=(ans*t)%mo;
}
cout<<ans;
return 0;
}