[组合数]求组合数的几种方法总结
2017-05-10 17:00
295 查看
参考:http://blog.csdn.net/u010582475/article/details/47707739
后面自己加了不少东西
求C(n,m)%mod的方法总结
1.当n,m都很小的时候可以利用杨辉三角直接求。
C(n,m)=C(n-1,m)+C(n-1,m-1);
2.利用乘法逆元。
乘法逆元:(a/b)%mod=a*(b^(mod-2)) mod为素数。
逆元可以利用扩展欧几里德或欧拉函数求得:
1).扩展欧几里德:b*x+p*y=1 有解,x就是所求
2).费马小定理:b^(p-1)=1(mod p),故b*b^(p-2)=1(mod p),所以x=b^(p-2)
1. n!/(m!*(n-m)! =x%p ,先对算出n!、m!、(n-m)!对p取模的余数,就转换为a/b=x%p;因为p为素数,所以等价于bx+py=a;然后用扩展的欧几里得定理算出 bx’+py’=1的解,x=x’*a,就得到了最终的x的值,即C(m,n)%p得值。
2.逆元 其实如果mod是素数 则b的逆元其实就是b^(mod-2)
也就是 (m!(n-m)!)的逆元为 (m!(n-m)!)p-2 ;
3.当n和m比较大,mod是素数且比较小的时候(10^5左右),通过Lucas定理计算
Lucas定理:A、B是非负整数,p是质数。A B写成p进制:A=a
a[n-1]…a[0],B=b
b[n-1]…b[0]。
则组合数C(A,B)与C(a
,b
)C(a[n-1],b[n-1])…*C(a[0],b[0]) mod p同余
即:Lucas(n,m,p)=C(n%p,m%p)*Lucas(n/p,m/p,p)
对了 除了lucas 还有o1 预处理逆元的方法 操作比lucas简单。lucas 主要是用于 C n m. n和m都很大的情况 只能用lucas变小
后面自己加了不少东西
求C(n,m)%mod的方法总结
1.当n,m都很小的时候可以利用杨辉三角直接求。
C(n,m)=C(n-1,m)+C(n-1,m-1);
2.利用乘法逆元。
乘法逆元:(a/b)%mod=a*(b^(mod-2)) mod为素数。
逆元可以利用扩展欧几里德或欧拉函数求得:
1).扩展欧几里德:b*x+p*y=1 有解,x就是所求
2).费马小定理:b^(p-1)=1(mod p),故b*b^(p-2)=1(mod p),所以x=b^(p-2)
1. n!/(m!*(n-m)! =x%p ,先对算出n!、m!、(n-m)!对p取模的余数,就转换为a/b=x%p;因为p为素数,所以等价于bx+py=a;然后用扩展的欧几里得定理算出 bx’+py’=1的解,x=x’*a,就得到了最终的x的值,即C(m,n)%p得值。
2.逆元 其实如果mod是素数 则b的逆元其实就是b^(mod-2)
也就是 (m!(n-m)!)的逆元为 (m!(n-m)!)p-2 ;
int inv(int a) { //return fpow(a, MOD-2, MOD); return a == 1 ? 1 : (long long)(MOD - MOD / a) * inv(MOD % a) % MOD; } LL C(LL n,LL m) { if(m < 0)return 0; if(n < m)return 0; if(m > n-m) m = n-m; LL up = 1, down = 1; for(LL i = 0 ; i < m ; i ++){ up = up * (n-i) % MOD; down = down * (i+1) % MOD; } return up * inv(down) % MOD; }
3.当n和m比较大,mod是素数且比较小的时候(10^5左右),通过Lucas定理计算
Lucas定理:A、B是非负整数,p是质数。A B写成p进制:A=a
a[n-1]…a[0],B=b
b[n-1]…b[0]。
则组合数C(A,B)与C(a
,b
)C(a[n-1],b[n-1])…*C(a[0],b[0]) mod p同余
即:Lucas(n,m,p)=C(n%p,m%p)*Lucas(n/p,m/p,p)
LL fact[maxn+5]; LL a[maxn+10]; LL inv[maxn+10]; void init(){ a[0] = a[1] = 1; fact[0] = fact[1] = 1; inv[1] = 1; for(int i = 2; i <= 100005; i++) { fact[i] = fact[i-1] * i % mod; inv[i] = (mod - mod/i)*inv[mod%i]%mod; a[i] = a[i-1] * inv[i] % mod; } } LL C(int n, int m){ return fact *a[n-m]%mod*a[m]%mod; }
对了 除了lucas 还有o1 预处理逆元的方法 操作比lucas简单。lucas 主要是用于 C n m. n和m都很大的情况 只能用lucas变小
void init(){ fact[0] = 1; for(int i = 1; i <= maxn; ++i) fact[i] = fact[i-1]*i%mod; inv[maxn]=quickM(fact[maxn],mod-2); for(int i=maxn-1;i>=0;i--) { inv[i]=inv[i+1]*(i+1); inv[i]%=mod; } } LL C(int n, int m){ return ((fact *inv[m])%mod*(inv[n-m]))%mod; }
相关文章推荐
- [组合数]求组合数的几种方法总结
- [组合数]求组合数的几种方法总结
- [组合数]求组合数的几种方法总结
- 求组合数的几种方法总结
- 总结有关Javascript链接的几种方法
- 总结C#中窗体间传递数据的几种方法 (由别人的方法整理)
- ASP.NET页面间的传值的几种方法(总结)
- 【续】用ObjectDataSource实现自定义分页的心得总结,几种分页方法的性能对比
- VLAN间访问控制的几种方法总结.
- 关闭和启动Oracle的几种方法总结
- asp获取URL参数的几种方法分析总结[原创]_应用技巧_脚本之家
- 总结一下最近一段时间导出数据到Excel的几种方法
- ASP.NET页面间的传值的几种方法(总结)
- 打开窗体的几种方法、注意事项的总结
- JAVA获得数字(正数和倒数)第几位的几种方法总结和对比
- ASP.NET输出EXCEL表格的几种方法(总结修改)
- ASP.NET页面间的传值的几种方法(总结)
- ASP.NET输出EXCEL表格的几种方法(总结修改)
- PHP下几种删除目录的方法总结
- asp.net中避免操作重复提交的几种方法总结: