博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[笔记]ACM笔记 - 组合数
阅读量:5033 次
发布时间:2019-06-12

本文共 2757 字,大约阅读时间需要 9 分钟。

一、高中数学公式复习

Cmn=n!m!(nm)!

Cmn=Cnmn=Cmn1+Cm1n1

C0n+C1n+C2n+...+Cnn=ni=0Cin=2n

C0n+C2n+C4n+...=C1n+C3n+C5n+...=2n1

Cmn+Cmn+1+Cmn+2+...+Cmn+m=mi=0Cmn+i=Cm+1n+m+1

kCkn=nCk1n1Cknk+1=Ck+1n+1n+1 (好吧这个没学过但是既然看到了就一并抄过来了)

二、快速求组合数取模C(n, m)%p

当n和p大小不同时方法有不同。

1. n很小,p随意,p不需要为素数

1) 原理

使用杨辉三角Cmn%p=(Cm1n1+Cmn1)%p

组合数C(n, m)其实就是杨辉三角第n行第m列的值(下标从0开始算的话)。每一行的各个值都是迭代上一行的结果。那么用二维数组打个表即可,for里套个for。

2) 我的模板

typedef long long lld;const int maxn = 1000+10;lld C_arr[maxn+10][maxn+10];void C_init(int n, int pr) {    for (int i = 0; i <= n; i++) {        C_arr[i][0] = C_arr[i][i] = 1;        for (int j = 1; j < i; j++)            C_arr[i][j] = (C_arr[i - 1][j - 1] + C_arr[i - 1][j]) % pr;    }}lld C(int n, int m) {    return C_arr[n][m];}

2. n相对小(方便打表),p可以很大,p要求为素数

1) 原理

仅使用费马小定理: 若p是质数,且a,p互质,那么 a的(p-1)次方除以p的余数恒等于1, 即a^(p-1) ≡ 1 (mod p),所以a^(p-2) ≡ 1/a (mod p)。所以a的逆元为a^(p-2)。于是将n!m!(nm)! 中的除法全变成了乘法:

得到公式:Cmn%p=((n!)%p[(nm)!]p2%p(m!)p2%p)%p

2) 我的模板

lld pow_mod(lld a, lld b, const int &pr){    lld ans = 1;    while (b) {        if (b & 1) ans = ans * a % pr;        b >>= 1;        a = a * a % pr;    }    return ans;}lld C(int n, int m){    return fac(n) % p * pow_mod(fac(n - m), p - 2, p) * pow_mod(fac(m), p - 2, p);}

可以看到这里最麻烦的是求阶乘fac(n),如果n不大的话打表是极好的。n较大的话使用以下公式递归求得:

n!=n!(n2)!n2)![(n2)!]2=Cn/2n[(n2)!]2
具体以后再写一篇求阶乘。

lld C_small(lld n, lld m, const int &pr){    lld ans = 1;    for (int i = 1; i <= m; i++)    {        lld a = (n - m + i) % pr;        lld b = i % pr;        ans = ans * (a * pow_mod(b, pr - 2, pr) % pr) % pr; //Fermat Theory    }    return ans;}

这是不打表的版本(其实就是没打表而已,没什么区别)。

3. n很大时要求p较小(p<10^5),p要求为素数

1) 原理

使用Lucas定理Cmn%p=(Cm%pn%pCm/pn/p)%p

为什么要求p挺小,由公式就可以看出,p太大了的话Cm%pn%p也依然很大。Lucas定理用到了费马小定理,要求p为素数。对于每个Cm/pn/p,递归调用Lucas定理。可以看见n被p取模后很容易就变小了,所以要求p较小。
定理证明:

2) 我的模板

typedef long long lld;lld pow_mod(lld a, lld b, const int &pr){    lld ans = 1;    while (b) {        if (b & 1) ans = ans * a % pr;        b >>= 1;        a = a * a % pr;    }    return ans;}lld C_small(lld n, lld m, const int &pr){    lld ans = 1;    for (int i = 1; i <= m; i++)    {        lld a = (n - m + i) % pr;        lld b = i % pr;        ans = ans * (a * pow_mod(b, pr - 2, pr) % pr) % pr; //Fermat Theory    }    return ans;}lld C(lld n, lld m, const int &pr) // Lucas's theorem{    if (m == 0 || m == n) return 1;    return C_small(n % pr, m % pr, pr) * C(n / pr, m / pr, pr) % pr;}

C_small就是用求逆元求解,像法二一样做打表也是极好的。

如果n不大,p很大,用一下Lucas定理后也就相当于执行了法二,所以以后直接用Lucas即可。

三、Vandermonde恒等式

范德蒙(Vandermonde)恒等式

Ckn+m=i=0kCinCkim
其中k肯定得小于等于min(n,m)。
理解:从n个黑球、m个白球里找k个球有多少方式。
k=min(n,m)时,这里假设
m<n,那就是
k=m时,可以变个形:

Ckn+m=i=0kCinCmk+im=i=0mCinCim

那意义就是n个黑球和m白个球中各找0个、1个、2个……m个对应颜色的球,一共有多少方法。

例题链接:

转载于:https://www.cnblogs.com/xienaoban/p/6798058.html

你可能感兴趣的文章
百度编辑器UEditor ASP.NET示例Demo 分类: ASP.NET...
查看>>
JAVA 技术类分享(二)
查看>>
android客户端向服务器发送请求中文乱码的问
查看>>
BZOJ4977 八月月赛 Problem G 跳伞求生 set 贪心
查看>>
BZOJ2631 tree LCT
查看>>
Codechef FIBTREE 树链剖分 主席树 LCA 二次剩余 快速幂
查看>>
UOJ#220. 【NOI2016】网格 Tarjan
查看>>
取消事件冒泡
查看>>
Symfony翻译教程已开课
查看>>
Python模块之pickle(列表,字典等复杂数据类型与二进制文件的转化)
查看>>
通过数据库表反向生成pojo类
查看>>
Jsoup操作
查看>>
android优化内存_下
查看>>
js 多张爆炸效果轮播图
查看>>
两个input放一行不能对齐
查看>>
实训作业2
查看>>
带权最短路 Dijkstra, SPFA, Bellman-Ford, ASP, Floyd-Warshall 算法分析
查看>>
css_去掉默认样式
查看>>
关于php开发中的字符编码问题总结的几个要点
查看>>
【原创】uwsgi中多进程+多线程原因以及串行化accept() - thunder_lock说明
查看>>