① 1<a<b<2000![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/854133b190d81788d8b826409dacaf6e.png)
利用组合数的性质进行简单的递推预处理就可以快速求出组合数
需要注意的是当b为0时,C b a是1
#include<iostream>
using namespace std;
const int N=2020;
int c[N][N];
int mod=1e9+7;
void intial()
{
for(int i=0;i<N;i++)
for(int j=0;j<=i;j++)
if(!j) c[i][j]=1;
else c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
int main()
{
intial();
int n;
cin>>n;
while(n--)
{
int a,b;
scanf("%d%d",&a,&b);
cout<<c[a][b]<<endl;
}
}
②1<a<b<1e5
将各个数的阶乘和逆元模上p的值先预处理出来,在直接利用组合数的计算公式求就行了。
为什么要进行取逆元的操作呢?
因为进行了除法运算在取模的情况下不等价,所以需要利用逆元求解。
注:某个数的逆元就是这个数的p-2次方
#include<iostream>
using namespace std;
const int N=100010,mod=1e9+7;
long long f[N],nif[N];
long long quick_power(int a,int b,int p)
{
long long res=1;
while(b)
{
if(b&1) res=(long long)res*a%p;
a=(long long)a*a%p;
b>>=1;
}
return res;
}
int main()
{
f[0]=nif[0]=1;
for(int i=1;i<N;i++)
{
f[i]=(long long)f[i-1]*i%mod;
nif[i]=(long long)nif[i-1]*quick_power(i,mod-2,mod)%mod;
}
int n;
cin>>n;
while(n--)
{
int a,b;
cin>>a>>b;
cout<<(long long)f[a]*nif[a-b]%mod*nif[b]%mod<<endl;
}
}
③1<b<a<1e18
当数据范围有第三种情况这么大的时候,只能利用卢卡斯定理了。
能处理的数据范围变大了,不过运行速度因为需要递归处理所以也变慢了,所以数据范围没有特别大的情况下,用上面两种方法其实更好
#include<iostream>
using namespace std;
typedef long long LL;
long long quick_power(long long a,long long b,int p)
{
long long res=1;
while(b)
{
if(b&1) res=(long long )res*a%p;
a=(long long)a*a%p;
b>>=1;
}
return res;
}
long long C(long long a,long long b,int p)
{
if (b > a) return 0;
long long res=1;
for(int i=1,j=a;i<=b;i++,j--)
{
res=(long long )res*j%p;
res=(long long )res*quick_power(i,p-2,p)%p;
}
return res;
}
long long lucas(long long a,long long b,int p)
{
if(a<p&&b<p) return C(a,b,p);
return (long long)lucas(a/p,b/p,p)*C(a%p,b%p,p)%p;//因为a和b很大,所以即使/p还是有可能不能直接计算,所以需要递归调用lucas这个函数
}
int main()
{
int n;
cin>>n;
while(n--)
{
long long a,b;int p;
cin>>a>>b>>p;
cout<<lucas(a,b,p)<<endl;
}
return 0;
}
参考文献 acwing 算法基础课