Cmn=n!m!(n−m)!
Cmn=Cn−mn=Cmn−1+Cm−1n−1
C0n+C1n+C2n+...+Cnn=∑ni=0Cin=2n
C0n+C2n+C4n+...=C1n+C3n+C5n+...=2n−1
Cmn+Cmn+1+Cmn+2+...+Cmn+m=∑mi=0Cmn+i=Cm+1n+m+1
kCkn=nCk−1n−1, Cknk+1=Ck+1n+1n+1 (好吧这个没学过但是既然看到了就一并抄过来了)
当n和p大小不同时方法有不同。
使用杨辉三角:Cmn%p=(Cm−1n−1+Cmn−1)%p 组合数C(n, m)其实就是杨辉三角第n行第m列的值(下标从0开始算的话)。每一行的各个值都是迭代上一行的结果。那么用二维数组打个表即可,for里套个for。
仅使用费马小定理: 若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!(n−m)! 中的除法全变成了乘法: 得到公式:Cmn%p=((n!)%p∗[(n−m)!]p−2%p∗(m!)p−2%p)%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; }这是不打表的版本(其实就是没打表而已,没什么区别)。
使用Lucas定理:Cmn%p=(Cm%pn%pCm/pn/p)%p 为什么要求p挺小,由公式就可以看出,p太大了的话Cm%pn%p也依然很大。Lucas定理用到了费马小定理,要求p为素数。对于每个Cm/pn/p,递归调用Lucas定理。可以看见n被p取模后很容易就变小了,所以要求p较小。 定理证明:网上看到的大神的博客
C_small就是用求逆元求解,像法二一样做打表也是极好的。 如果n不大,p很大,用一下Lucas定理后也就相当于执行了法二,所以以后直接用Lucas即可。
范德蒙(Vandermonde)恒等式:
Ckn+m=∑i=0kCinCk−im 其中k肯定得小于等于min(n,m)。 理解:从n个黑球、m个白球里找k个球有多少方式。 当 k=min(n,m)时,这里假设 m<n,那就是 k=m时,可以变个形:Ckn+m=∑i=0kCinCm−k+im=∑i=0mCinCim
那意义就是n个黑球和m白个球中各找0个、1个、2个……m个对应颜色的球,一共有多少方法。 例题链接:Codeforces 785D - Anton and School - 2
转载于:https://www.cnblogs.com/xienaoban/p/6798058.html