模板 - 组合数学 - (新)
Posted yinku
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模板 - 组合数学 - (新)相关的知识,希望对你有一定的参考价值。
相关知识点应在《组合数学》中寻找,而不是在模板中寻找。
update1:通过【模板】卢卡斯定理的验证。
全部模板,卢卡斯定理默认装载重新求组合数。
namespace combinatorics{
//注意需要init(),必要时修改常量
const ll MOD=1e9+7;
const int MAXN=2000000;
ll inv[MAXN+5],fac[MAXN+5],invfac[MAXN+5];
//1. 快速幂 x^n %mod
inline ll qpow(ll x,ll n,ll mod=MOD) {
ll res=1%mod;
while(n) {
if(n&1)
res=res*x%mod;
x=x*x%mod;
n>>=1;
}
return res;
}
//2. 快速乘 a*b %mod 防止乘法溢出ll
inline ll qmut(ll a,ll b,ll mod=MOD) {
ll res=0;
while(b) {
if(b&1)
res=(res+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return res;
}
//3. 乘法逆元 快速幂+费马小定理,要求p必须是质数 (依赖1. 快速幂)
inline ll inv_p(ll n,ll p=MOD) {
return qpow(n,p-2);
}
//4. 扩展欧几里得算法:返回 g=gcd(a,b) ,以及对应的等式 ax+by=g 的解
ll exgcd(ll a,ll b,ll &x,ll &y) {
if(!a&&!b)
return -1;
if(!b) {
x=1,y=0;
return a;
}
ll d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
//5. 扩展欧几里得算法求逆元,只要求 a,m 互质
inline ll inv_rp(ll a,ll mod=MOD) {
ll x,y;
ll d=exgcd(a,mod,x,y);
if(d==1)
return (x%mod+mod)%mod;
return -1;
}
//6. 线性求乘法逆元
void init_inv(int n=MAXN,ll mod=MOD) {
inv[1]=1;
for(int i=2; i<=n; i++) {
inv[i]=inv[mod%i]*(mod-mod/i)%mod;
}
}
//7. 线性求阶乘,阶乘乘法逆元 (依赖6. 线性求乘法逆元)
void init_fac_invfac(int n=MAXN,ll mod=MOD) {
.//这个点用来触发编译错误,提示使用init(),并修改MAXN和MOD
init_inv(n);
fac[0]=1,invfac[0]=1;
for(int i=1; i<=n; i++) {
fac[i]=fac[i-1]*i%mod;
invfac[i]=invfac[i-1]*inv[i]%mod;
}
}
//8. 利用阶乘和阶乘逆元计算排列数A_n^m %mod (依赖7. 线性求阶乘,阶乘乘法逆元)
inline ll A(ll n,ll m,ll mod=MOD) {
return fac[n]*invfac[n-m]%mod;
}
//9. 直接计算排列数A_n^m %mod
ll A_2(ll n,ll m,ll mod=MOD){
if(m>n) return 0;
ll u=1;
for(int i=n-m+1;i<=n;i++)
u=u*i%mod;
return u;
}
//10. 利用阶乘和阶乘逆元计算组合数C_n^m %mod (依赖7. 线性求阶乘,阶乘乘法逆元)
inline ll C(ll n,ll m,ll mod=MOD) {
return fac[n]*invfac[n-m]%mod*invfac[m]%mod;
}
//11. 直接计算组合数C_n^m %mod
ll C_2(ll n,ll m,ll mod=MOD){
if(m>n) return 0;
ll u=1,d=1;
for(int i=n-m+1;i<=n;i++)
u=u*i%mod;
for(int i=1;i<=m;i++)
d=d*i%mod;
//为下面的inv装入正确的乘法逆元,默认使用扩展欧几里得算法重新求解
//可以视情况换用更快的init()后的inv[d],或者费马小定理(一般不必要
return u*inv_rp(d,mod)%mod;
}
//12. 卢卡斯定理计算组合数C_n^m%p,p是质数 (依赖10. /11. 计算组合数)
inline ll Lucas(ll n,ll m,ll p=MOD) {
if(m>n)
return 0;
ll ans=1;
for(; m; n/=p,m/=p){
//当p并非默认参数MOD时,必须使用直接计算组合数的C_2
ans=ans*C_2(n%p,m%p,p)%p;
//当p为默认参数MOD时,使用init()后的O(1)求组合数
//ans=ans*C(n%p,m%p)%p;
}
return ans;
}
};
using namespace combinatorics;
//注意需要init(),必要时修改常量
精简模板,需要保证固定MOD为质数,卢卡斯定理默认装载快速求组合数。
namespace combinatorics{
//注意需要init(),必要时修改常量
const ll MOD=1e9+7;
const int MAXN=2000000;
ll inv[MAXN+5],fac[MAXN+5],invfac[MAXN+5];
//1. 快速幂 x^n %mod
inline ll qpow(ll x,ll n,ll mod=MOD) {
ll res=1%mod;
while(n) {
if(n&1)
res=res*x%mod;
x=x*x%mod;
n>>=1;
}
return res;
}
//3. 乘法逆元 快速幂+费马小定理,要求p必须是质数 (依赖1. 快速幂)
inline ll inv_p(ll n,ll p=MOD) {
return qpow(n,p-2);
}
//6. 线性求乘法逆元
void init_inv(int n=MAXN,ll mod=MOD) {
inv[1]=1;
for(int i=2; i<=n; i++) {
inv[i]=inv[mod%i]*(mod-mod/i)%mod;
}
}
//7. 线性求阶乘,阶乘乘法逆元 (依赖6. 线性求乘法逆元)
void init_fac_invfac(int n=MAXN,ll mod=MOD) {
.//这个点用来触发编译错误,提示使用init(),并修改MAXN和MOD
init_inv(n);
fac[0]=1,invfac[0]=1;
for(int i=1; i<=n; i++) {
fac[i]=fac[i-1]*i%mod;
invfac[i]=invfac[i-1]*inv[i]%mod;
}
}
//8. 利用阶乘和阶乘逆元计算排列数A_n^m %mod (依赖7. 线性求阶乘,阶乘乘法逆元)
inline ll A(ll n,ll m,ll mod=MOD) {
return fac[n]*invfac[n-m]%mod;
}
//10. 利用阶乘和阶乘逆元计算组合数C_n^m %mod (依赖7. 线性求阶乘,阶乘乘法逆元)
inline ll C(ll n,ll m,ll mod=MOD) {
return fac[n]*invfac[n-m]%mod*invfac[m]%mod;
}
//12. 卢卡斯定理计算组合数C_n^m%p,p是质数 (依赖10. /11. 计算组合数)
inline ll Lucas(ll n,ll m,ll p=MOD) {
if(m>n)
return 0;
ll ans=1;
for(; m; n/=p,m/=p){
//当p并非默认参数MOD时,必须使用直接计算组合数的C_2
//ans=ans*C_2(n%p,m%p,p)%p;
//当p为默认参数MOD时,使用init()后的O(1)求组合数
ans=ans*C(n%p,m%p)%p;
}
return ans;
}
};
using namespace combinatorics;
//注意需要init(),必要时修改常量
以上是关于模板 - 组合数学 - (新)的主要内容,如果未能解决你的问题,请参考以下文章