组合数模板

逆元求组合数

预处理阶乘和阶乘的逆元, O ( 1 ) O(1) O(1) 求组合数
f a c fac fac i n v inv inv 数组必须开 l o n g long long l o n g long long

void init()
{
	fac[0] = inv[0] = 1;
	fac[1] = inv[1] = 1;
	for(int i = 2; i <= maxn - 9; ++i)// 一定要从2开始
		fac[i] = fac[i-1] * i % mod,
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
	for(int i = 1; i <= maxn - 9; ++i)
		inv[i] = inv[i] * inv[i-1] % mod;
}
ll C(ll n, ll m){
	if(m > n) return 0ll;
	return fac[n] * inv[m] % mod * inv[n-m] % mod;
}

打表求组合数(不用求逆元

可以用于解决组合数取模时,模数为合数,无法用逆元求解的情况
当然 n n n k k k 必须比较小
递归

ll C(ll n,ll k){// 记忆化递归
	if(c[n][k]) return c[n][k];
	if(n == k || k == 0) return c[n][k] = 1;
	return c[n][k] = (C(n-1, k) + C(n-1, k-1)) % mod;
}

非递归

const int maxn(1005), mod(100003);
int tC[maxn][maxn]; //tC 表示 table of C

inline int C(int n, int k)
{
    if(k > n) return 0;
    return tC[n][k];
}

void calcC(int n)
{
    for(int i = 0; i < n; i++)
    {
        tC[i][0] = 1;
        for(int j = 1; j <= i; j++)
            tC[i][j] = (C(i - 1, j - 1) + C(i - 1, j)) % mod;
    }
}

Lucas定理

参考博客
适用情况 C n m   %   m o d C_n^m \ \% \ mod Cnm % mod n n n 大于 m o d mod mod 的情况( m o d mod mod 一般不大于1e6)
模板

ll Lucas(ll n, ll m)
{
	if(!m) return 1;
	return Lucas(n / mod, m / mod) * C(n % mod, m % mod) % mod;
}

组合数看情况用哪个板子
每次只需要求 C n % m o d m % m o d C_{n\%mod}^{m\%mod} Cn%modm%mod,这样就大大减少了计算量

例题

P3807 【模板】卢卡斯定理/Lucas 定理
题意:
给定三个数, n , m , p n,m,p n,m,p 的值,求 C n + m n m o d    n C_{n+m}^{n} \mod n Cn+mnmodn 的值, p p p 是质数
1 < = n , m , p < = 1 0 5 1<=n,m,p<=10^5 1<=n,m,p<=105
code:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
const int maxn = 2e5 + 9;
//const int mod = 998244353;
ll n, m;
ll mod;
ll fac[maxn], inv[maxn];
ll q_pow(ll a, ll n, ll ans = 1){
	while(n){
		if(n & 1) ans=ans*a%mod;a=a*a%mod;n>>=1;
	}return ans;
}
ll C(ll n, ll m)
{
	if(m > n) return 0;
	return fac[n] * q_pow(fac[m], mod - 2) % mod * q_pow(fac[n-m], mod - 2) % mod;
}
ll Lucas(ll n, ll m)
{
	if(!m) return 1;
	return Lucas(n / mod, m / mod) * C(n % mod, m % mod) % mod;
}
void work()
{
	cin >> n >> m >> mod;
	fac[0] = 1;
	for(int i = 1; i <= n + m; ++i)
		fac[i] = fac[i-1] * i % mod;
	cout << Lucas(n + m, m) << endl;
}


int main()
{
	ios::sync_with_stdio(0);
	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值