[YM]模板-组合数

概念:

相信到了数学和数论这一块脱离不了组合的存在

组合数运算

(1)C(n,m)=C(n-1,m)+C(n-1,m-1)

(2)C(n,m)=\frac{n!}{(n-m)!m!}

求组合数的求法有很多

适用的范围也是不一样的

模板:

杨辉递推法:

for(int i=0;i<=N;i++)
   for(int j=0;j<=i;j++)
      c[i][j]=((j==0||j==i)?1:(c[i-1][j]+c[i-1][j-1])%q);

适用于数据范围小,使用度密集

这里使用度就是使用每个组合数的频率

q是模数

N是数据范围

复杂度O(n^2)

公式法:

const int N =1e5 + 5;
int mod=1e9+7;
ll qpow(ll a,ll b){
	ll ans=1;
	a%=mod;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1; 
	}
	return ans;
}
ll inv(ll a){return qpow(a,mod-2);}
ll jc[N];
ll C(ll n,ll m){return jc[n]*inv(jc[n-m])%mod*inv(jc[m])%mod;}
void solve(){
	jc[0]=1;
	for(int i=1;i<=N;i++) jc[i]=jc[i-1]*i%mod;  //预处理阶乘
	cout<<C(4,2);
}

适用于数据范围大,且使用度稀疏

求一次组合数的复杂度为O(logn)

在大数取模除法中我们通常都用逆元代替

(a/b)%mod=a*inv(b)%mod

剩下的就是直接使用公式求即可

线性逆元法:

ll qpow(ll a, ll b) { ll ans = 1; a %= mod; while (b) { if (b & 1)ans = ans * a % mod; a = a * a % mod; b >>= 1; }return ans; }
ll inv(ll a) { return qpow(a, mod - 2); }
vector<ll>jc,iv;
ll C(ll n, ll m){return jc[n] * iv[m] % mod * iv[n - m] % mod;}
void init(int n){
    jc.resize(n+1,0);
    iv.resize(n+1,0);
    jc[0]=jc[1]=1;
    iv[0]=iv[1]=1;
    for (ll i = 2;i<=n;i++){
        jc[i]=jc[i-1]*i%mod;
        iv[i]=iv[i-1]*inv(i)%mod;
    }
}

适用于数据范围大,且使用度密集

这也是最常用的方法

复杂度为O(nlogn)

实质就是将每个阶乘和逆元预处理出来

然后O(1)使用公式

这里线性逆元可以参考:

洛谷:P3811 【模板】模意义下的乘法逆元 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值