求C(n,m) //其中n>=m
一、暴力
1.按定义直接计算:
n,m<15
long long的大小制约着n,m,如果加上mod,n,m可以再高些。
ll C(int n,int m) //暴力
{
if(n>m) return 0;
if(n<m-n) n=m-n;
ll ans=1;
for(int i=n+1;i<=m;i++) ans*=i;
for(int i=1;i<=m-n;i++) ans/=i;
return ans;
}
2.二维数组打表:
n,m<5000
通过递推公式实现:com[i][j]=com[i-1][j]+com[i-1][j-1];
#define ll long long
#define M 5000
ll com[M][M];
void Prons()
{
com[0][1]=1;
for(int i=1;i<M;i++) //i>j
for(int j=0;j<=i;j++){
if(j==0) com[i][j]=1;
else com[i][j]=com[i-1][j]+com[i-1][j-1];
}
}
特点:优点是简单短,缺点是时间慢,范围小
二、利用快速幂
3.快速幂+逆元
n,m<1e6
仍然是利用 C(n,m)=n!/[m!*(n-m)!] 的计算,预处理阶乘数组降低时间,qpow是逆元
ll fac[1000005]; //阶乘
void init(){
fac[0]=1;
for(int i=1;i<1e6;i++)
fac[i]=(fac[i-1]*i)%mod;
}
ll qpow(ll a,ll b)
{
ll ans=1;
a%=mod;
while(b){
if(b&1){
ans=ans*a%mod;
b--;
}
b>>= 1;
a=a*a%mod;
}
return ans;
}
ll C(int n,int m){
return fac[n]*qpow(fac[n-m],mod-2)%mod*qpow(fac[m],mod-2)%mod;
}
特点:时间复杂度是log2,如果符合范围,这个是时间最快的。这个也是最好用的
4.卢斯卡定理1:
通过卢思卡公式实现
p为质数 n≤1e18,m≤1e6,mod≤1e9(可以是1e9+7)
#define ll long long
#define M 100005
const ll mod=10007;
ll a,b;
ll qpow(ll a,ll b)
{
ll ans=1;
a%=mod;
while(b)
{
if(b&1)
{
ans=ans*a%mod;
b--;
}
b>>= 1;
a=a*a%mod;
}
return ans;
}
ll C(ll n,ll m)
{
if(m>n) return 0;
ll ans=1;
for(int i=1;i<=m;i++)
{
ll a=(n+i-m)%mod;
ll b=i%mod;
ans=ans*(a*qpow(b,mod-2)%mod)%mod;
}
return ans;
}
ll Lucas(ll n,ll m) //n>m
{
if(m==0) return 1;
return C(n%mod,m%mod)*Lucas(n/mod,m/mod)%mod;
}
特点:这个比下面的常用些,mod可以取到1e9+7。
n,m比较大的情况下可以用,如果n,m都<1e6,还是建议用上的面,因为可能会超时。
5.卢斯卡定理2:
m≤n≤1e18,mod≤1e6
#define ll long long
#define M 1000005
ll a,b,mod;
ll f[M];
void init(){
f[0]=1;
for(int i=1;i<=mod;++i)
f[i]=f[i-1]*i%mod;
}
ll qpow(ll a, ll b){
ll ans=1;
a%=mod;
while(b)
{
if(b&1)
{
ans=ans*a%mod;
b--;
}
b>>= 1;
a=a*a%mod;
}
return ans;
}
ll Lucas(ll n,ll k){
ll ret=1;
while(n&&k){
ll nn=n%mod,kk=k%mod;
if(nn<kk) return 0;
ret=ret*f[nn]*qpow(f[kk]*f[nn-kk]%mod,mod-2)%mod;
n/=mod;
k/=mod;
}
return ret;
}
特点:卢斯卡定理适用于n,m超大的情况,但是时间复杂度并不是很好,有两种可供选择。
具体的知识没有相关了解,建议自己去搜搜,本文只做适用范围总结