组合数学(推导+模板)

组合数

C n m = C n − 1 m + C n − 1 m − 1 C_n^m=C_{n-1}^{m}+C_{n-1}^{m-1} Cnm=Cn1m+Cn1m1

C n m = C n n − m C_n^m=C_n^{n-m} Cnm=Cnnm

杨辉三角递推式

O(n2)

012345678910
01
111
2121
31331
414641
515101051
61615201561
7172135352171
818285670562881
9193684126126843691
101104512021025221012045101

img

//范围小数大
int c[1001][1001];
void solve()
{
    for (int i = 0; i <= 10;i++){
        for (int j = 0; j <= i;j++){
            if(j==0){
                c[i][j] = 1;
            }
            else{
                c[i][j] = (c[i - 1][j - 1] + c[i - 1][j])%mod;
            }
        }
    }
}
快速幂&逆元

O(nlogp)

int fac[N], pr[N];//fac阶乘数组,pr逆元数组
int ksm(int d,int c,int mod){
    int ans = 1;
    while(c){
        if(c&1){
            ans = ans * d % mod;
        }
        c >>= 1;
        d = d * d % mod;
    }
    return ans;
}
void init(){
    // n<=1e7;
    fac[0] = pr[0] = 1;
    for (int i = 1; i <= ;i++){
        fac[i] = fac[i - 1] * i % mod;
        pr[i] = pr[i - 1] * ksm(i, mod - 2, mod)%mod;
    }
}
int c(int n,int k){
    return n < k ? 0 : ((fac[n] * pr[k]) % mod) * pr[n - k] % mod;
}

递推法逆元

O(n)
p ≡ 0 ( m o d p ) p\equiv0\pmod p p0(modp)

设 p = k ∗ i + r ( 1 < r < i < p ) 设p=k*i+r(1<r<i<p) p=ki+r(1<r<i<p)

k ∗ i + r ≡ 0 ( m o d p ) k*i+r\equiv0 \pmod p ki+r0(modp)

两边同乘 i − 1 , r − 1 得 两边同乘i^{-1},r^{-1}得 两边同乘i1,r1

k ∗ r − 1 + i − 1 ≡ 0 ( m o d p ) k*r^{-1}+i^{-1}\equiv0\pmod p kr1+i10(modp)

i − 1 ≡ − k ∗ r − 1 ( m o d p ) i^{-1}\equiv -k*r^{-1}\pmod p i1kr1(modp)

i − 1 ≡ p ∗ r − 1 − k ∗ r − 1 ( m o d p ) i^{-1}\equiv p*r^{-1}-k*r^{-1} \pmod p i1pr1kr1(modp)

i − 1 ≡ ( p − k ) ∗ r − 1 ( m o d p ) i^{-1}\equiv(p-k)*r^{-1}\pmod p i1(pk)r1(modp)

i n v [ i ] = ( p − p / i ) ∗ i n v [ p % i ] % p inv[i]=(p-p/i)*inv[p\%i]\%p inv[i]=(pp/i)inv[p%i]%p

int fac[N],invfac[N],pr[N];
void init(){
    fac[1] = invfac[1] = invfac[0] = pr[1] = fac[0] = 1;
    //<=1e7
    for (int i = 2; i <= ;i++){
        fac[i] = fac[i - 1] * i%mod;
        pr[i] = pr[mod%i] * (mod - mod / i) % mod;
        invfac[i] = invfac[i - 1] * pr[i] % mod;
    }
}
int c(int n,int k){
    return n < k ? 0 : ((fac[n] * invfac[k]) % mod) * invfac[n - k] % mod;
}
/*
pr数组是一个用于计算模逆元的数组。模逆元是指在模运算下的乘法逆元,即对于给定的模数mod,对于任意非零整数a,存在一个整数b,使得a * b ≡ 1 (mod mod)。在这段代码中,pr数组用于存储每个数的模逆元。
具体来说,pr[i] 存储了 (mod - mod/i) * pr[mod%i] % mod 的结果。其中,pr[1] 被初始化为 1,而其余位置 i 的值通过递推计算得到。这样,通过查询 pr 数组可以获得任意数的模逆元。
*/
卢卡斯lucas定理

O(logpN⋅(p+log2p))

此方法用于非常大的 n 和 m ,但是 p 却比较小的计算。
C n m = C p n p m × C n % p m % p ( m o d p ) C_n^m =C_ \frac{p} {n}^\frac{p}{m} \times C_{n \% p} ^{m\%p}\pmod p Cnm=Cnpmp×Cn%pm%p(modp)

引理1:
C p x ≡ 0 ( m o d p ) ( 0 < x < p ) C_p^x \equiv 0 \pmod p (0<x<p) Cpx0(modp)(0<x<p)

因 C p x = P ! x ! ( p − x ) ! = p ( p − 1 ) ! x ( x − 1 ) ! ( p − x ) ! = p x C p i x i 因 C_p^x= \frac {P!}{x!(p-x)!}=\frac{p(p-1)!}{x(x-1)!(p-x)!}=\frac{p}{x}C_p^ix^i Cpx=x!(px)!P!=x(x1)!(px)!p(p1)!=xpCpixi

故 C p x ≡ p × i n v ( x ) C p − 1 x − 1 ≡ 0 ( m o d p ) 故C_p^x\equiv p \times inv(x) C_{p-1}^{x-1}\equiv0\pmod p Cpxp×inv(x)Cp1x10(modp)

引理2:

( 1 + x ) p ≡ 1 + x p ( m o d p ) (1+x)^p \equiv 1+x^p \pmod p (1+x)p1+xp(modp)

由二项式定理 ( 1 + x ) p ≡ ∑ i = 0 p C p i x i 由二项式定理(1+x)^p\equiv \sum_{i=0}^pC_p^ix^i 由二项式定理(1+x)pi=0pCpixi

由引理 1 可知,只剩 i = 0 ( C p 0 ) , p ( C p p ) 两项,得证 由引理1可知,只剩i=0(C_p^0),p(C_p^p)两项,得证 由引理1可知,只剩i=0(Cp0),p(Cpp)两项,得证

推导证明
令 n = a p + b , m = c p + d 令n = ap + b,m = cp + d n=ap+b,m=cp+d

( 1 + x ) n = C n 0 x 0 + c n 1 x 1 + . . . + C n k x k + C n n x n ( m o d p ) (1+x)^n = C_n^0x^0+c_n^1x^1+...+C_n^kx^k+C_n^nx^n \pmod p (1+x)n=Cn0x0+cn1x1+...+Cnkxk+Cnnxn(modp)

( 1 + x ) n ≡ ∑ i = 0 n C n i x i ( m o d p ) (1+x)^n \equiv \sum_{i=0}^n C_n^ix^i \pmod p (1+x)ni=0nCnixi(modp)

≡ ( 1 + x ) a p + b \equiv (1+x)^{ap+b} (1+x)ap+b

≡ ( ( 1 + x ) p ) a . ( 1 + x ) b \equiv ((1+x)^p)^a.(1+x)^b ((1+x)p)a.(1+x)b

≡ ∑ i = 0 a C a i x i p . ∑ j = 0 b C b j x j ( m o d p ) \equiv \sum_{i=0}^a C_a^ix^{ip}. \sum_{j=0}^bC_b^jx^j \pmod p i=0aCaixip.j=0bCbjxj(modp)

( 1 ) 式中 x m 的系数是 C n m (1)式中x^m的系数是C^m_n (1)式中xm的系数是Cnm

( 2 ) 式中 x m = x c p . x d 的系数为 C a c . C b d (2)式中x^m = x^{cp}.x^d的系数为C_a^c.C_b^d (2)式中xm=xcp.xd的系数为Cac.Cbd

故 C n m ≡ C a c C b d ( m o d p ) 故C_n^m \equiv C_a^cC_b^d \pmod p CnmCacCbd(modp)

a = n / p , b = m / p , c = n % p , d = m % p a=n/p,b=m/p,c=n\%p,d=m\%p a=n/p,b=m/p,c=n%p,d=m%p

即 C n m ≡ C n / p m / p C n % p m % p 即C_n^m \equiv C_{n/p}^{m/p}C_{n \% p}^{m \% p} CnmCn/pm/pCn%pm%p

int fac[N], pr[N];//fac阶乘数组,pr逆元数组
int ksm(int d,int c,int mod){
    int ans = 1;
    while(c){
        if(c&1){
            ans = ans * di % mod;
        }
        c >>= 1;
        d = d * d % mod;
    }
    return ans;
}
void init(){
    // n,m<=1e18;
    // mod<=1e6;
    fac[0] = pr[0] = 1;
    for (int i = 1; i <= mod;i++){
        fac[i] = fac[i - 1] * i % mod;
        pr[i] = pr[i - 1] * ksm(i, mod - 2, mod)%mod;
    }
}
int C(int n,int m){
    return fac[n] * pr[n - m] * pr[m]%mod;
}
int lucas(int n, int m)
{
    if (m == 0)
    {
        return 1;
    }
    return lucas(n / mod, m / mod) * C(n % mod, m % mod) % mod;
}
高精度组合数(不取模)

O(n)

C(5000,10000)是3009位

首先筛出5000以内的所有质数

再预 处 理 计 算 a 和 b 的 每 个 质 因 子 的 次 数

最后高精度计算结果
高精度计算 C n m = P 1 a 1 P 2 a 2 . . . P k a k 高 精 度 计 算 C_n^m=P_1^{a_1}P_2^{a_2}...P_k^{a_k} 高精度计算Cnm=P1a1P2a2...Pkak

故 a 1 个数等于 n ! 中的个数减去 ( n − m ) ! + m ! 中的个数 p 1 x p 1 y p 1 z 故a_1个数等于n!中的个数减去(n-m)!+m!中的个数 \frac {p^x_1}{p^y_1p^z_1} a1个数等于n!中的个数减去(nm)!+m!中的个数p1yp1zp1x

即 a 1 = x − y − z 即a_1=x-y-z a1=xyz

a 1 在 x ! 中的个数 s u m 1 = x p 1 + x p 1 2 + x p 1 3 + . . . + x p 1 k a_1在x!中的个数sum_1= \frac {x}{p_1}+\frac {x}{p^2_1}+\frac {x}{p^3_1}+...+\frac {x}{p^k_1} a1x!中的个数sum1=p1x+p12x+p13x+...+p1kx

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int prime[N], cnt, vis[N];
int c[N];
void init()
{
    for (int i = 2; i < N; ++i)
    {
        if (!vis[i])
            prime[cnt++] = i;
        for (int j = 0; i * prime[j] < N; ++j)
        {
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0)
                break;
        }
    }
}
int get(int n, int p)
{ // n!中p的个数
    int tot = 0;
    while (n)
        tot += n / p, n /= p;
    return tot;
}
int getc(int n, int m, int p)
{ // c中p的个数
    return get(n, p) - get(n - m, p) - get(m, p);
}
void mul(int c[], int p, int &len)
{ // 高精度乘法
    int t = 0;
    for (int i = 0; i < len; i++)
    {
        t += c[i] * p;
        c[i] = t % 10;
        t /= 10;
    }
    while (t)
    {
        c[len++] = t % 10;
        t /= 10;
    }
}
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    init();
    int n, m;
    cin >> n >> m;
    int len = 1;
    c[0] = 1;
    for (int i = 0; i < cnt; ++i)
    {
        int p = prime[i];
        int s = getc(n, m, p);
        while (s--)
            mul(c, p, len);
    }
    for (int i = len - 1; i >= 0;i--)
        cout << c[i];
    cout << '\n';
    return 0;
}
  • 50
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值