排列组合(卢卡斯定理)
作者:互联网
终终终.......于要对组合数学下手了
我们来看一下这个 (在a个苹果中选出b个苹果)
根据以上两种情况:
我们可以得到这个递推式:
我们知道,那么任何一个都可以由更小的递推出来,直接上代码:
void init()
{
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] + c[i-1][j-1])% mod;
}
}
}
第二种算法:
没错这就是官方公式
我们直接从公式下手,除法的话,我们用快速幂求逆元变成乘法来做。
上代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010,mod = 6662333;
//求逆元
int qmi(int a,int b,int p)
{
int res = 1;
while(b)
{
if(b&1)res = (LL)res * a %p;
a = (LL)a * a % p;
b>>=1;
}
return res;
}
LL n;
int res=1;
int main()
{
int a,b;
scanf("%d%d",&a,&b);
for(int i=1,j=a;i<=b;i++,j--)
{
res = (LL)res * j % mod;
res = (LL)res * qmi(i,mod-2,mod) % mod;
}
printf("%d\n",res);
return 0;
}
洛谷题:https://www.luogu.com.cn/problem/P3807
接下来我们的主角来了----卢卡斯定理(lucas)。
lucas定理:(p 是质数)
Lucas 定理专门用来求解大组合数取模的问题,但是模数必须为素数。
根据公式可以发现 中 a mod p 和 b mod p 一定是小于p的,所以我们可以根据之前求组合数的算法来求,后半部分使用递归的方式继续采用 lucas定理来求即可。
上代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int p,n;
//求逆元
int qmi(int a,int b)
{
int res = 1;
while(b)
{
if(b&1)res =(LL)res * a %p;
a =(LL) a * a % p;
b>>=1;
}
return res;
}
//求组合数
int C(int a,int b)
{
int res =1;
for(int i=1,j=a;i<=b;i++,j--)
{
res = (LL)res * j % p;
res = (LL)res * qmi(i,p-2) % p;
}
return res;
}
//lucas定理
int lucas(LL a,LL b)
{
if(a<p && b<p) return C(a,b);
else return (LL)C(a % p,b % p) * lucas(a / p,b / p) % p;
}
int main()
{
cin>>n;
while (n -- )
{
LL n,m;
cin>>n>>m>>p;
cout<<lucas(n+m,n)<<endl;
}
return 0;
}
get!
标签:int,res,定理,long,卢卡斯,排列组合,LL,mod 来源: https://blog.csdn.net/qq_47982709/article/details/120178285