lucas定理
来源:互联网 发布:c 编程软件 编辑:程序博客网 时间:2024/06/05 16:40
今天遇到一道组合数取模的题,毫无疑问爆零了,我出题人真是个sb,连Lucas定理都不知道QAQ
Lucas定理: 求解C(n,m)%p , n 和 m 是 非负整数 , p是素数
结论1:Lucas(n,m,p) = C(n%p,m%p)* Lucas(n/p,m/p,p)
结论2. 把n写成p进制a[n]a[n-1]a[n-2]...a[0],把m写成p进制b[n]b[n-1]b[n-2]...b[0],则C(n,m)与C(a[n],b[n])*C(a[n-1],b[n-1])*C(a[n-2],b[-2])*....*C(a[0],b[0])模p同余。
*注:n,m不能大于10^5,不大于情况下用逆元的方法可以解决,如果大了就不能解决。
/* * Created on: 2017-06-02 * Author: zjq * 求C(n,m)%p, p为素数 * Lucas定理: Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p); */#include <cstdio>#include <cstring>#include <cmath>#include <iostream>#include <algorithm>using namespace std;#define LL long longLL fac[20005];void init(LL p){ fac[0] =1; for(LL i =1; i <= p; i++) fac[i] = fac[i-1]*i % p;}LL exp_mod(LL a, LL b, LL p){ LL tmp = a % p, ans =1; while(b) { if(b & 1) ans = ans * tmp % p; tmp = tmp*tmp % p; b >>=1; } return ans;}LL C(LL n, LL m, LL p){ if(m > n) return 0; return fac[n]*exp_mod(fac[m]*fac[n-m], p-2, p) % p;//逆元}LL Lucas(LL n, LL m, LL p){ if(m ==0) return 1; return (C(n%p, m%p, p)*Lucas(n/p, m/p, p))%p;}int main(){freopen("comb.in","r",stdin);freopen("comb.out","w",stdout);LL n,m,p;scanf("%lld %lld %lld",&n,&m,&p);init(p);printf("%lld\n",Lucas(n,m,p)); return 0;}
另一种
现在目标是求
虽然有
所以需要把“除法”转换成“乘法”,才能借助取模的性质在不爆long long的情况下计算组合数。这时候就需要用到“逆元”!
逆元:对于a和p,若a*b%p≡1,则称b为a%p的逆元。
那这个逆元有什么用呢?试想一下求
那怎么求逆元呢?这时候就要引入强大的费马小定理!
费马小定理:对于a和素数p,满足$a^{p-1}$%p≡1
接着因为
所以问题就转换成求解
现在总结一下求解
- 通过循环,预先算好所有小于max_number的阶乘(%p)的结果,存到fac[max_number]里 (fac[i] = i!%p)
- 求m!%p的逆元(即求fac[m]的逆元):根据费马小定理,x%p的逆元为
xp−2 ,因此通过快速幂,求解fac[m]p−2 %p,记为M - 求(n-m)!%p的逆元:同理为求解
fac[n−m]p−2 %p,记为NM Cmn%p = ((fac[n]*M)%p*NM)%p
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> using namespace std; typedef long long ll; typedef long double ld; const ld eps=1e-10; const int inf = 0x3f3f3f; const int maxn = 100005; ll fac[maxn]; ll pow_mod(ll a,ll b,ll mod) { ll ret = 1; while(b) { if(b & 1) ret = (ret*a)%mod; a = (a*a)%mod; b >>= 1; } return ret; } ll Fac(ll p) { fac[0] = 1; for(int i = 1;i <= p;i++) { fac[i] = (fac[i-1] * i)%p; } } ll lucas(ll n,ll m,ll p) { ll ret = 1; while(n && m) { ll a = n % p; ll b = m % p; if(a < b) return 0; ret = (ret*fac[a]*pow_mod(fac[b]*fac[a-b]%p,p-2,p))%p; n /= p; m /= p; } return ret%p; } int main() { int t; scanf("%d",&t); while(t--) { ll a,b,p; scanf("%I64d%I64d%I64d",&a,&b,&p); Fac(p); ll ans = lucas(a+b,b,p); printf("%I64d\n",ans); } }
阅读全文
0 0
- Lucas 定理
- Lucas 定理
- lucas定理
- Lucas定理
- Lucas 定理
- Lucas 定理
- Lucas 定理
- Lucas 定理
- Lucas定理
- lucas定理
- lucas定理
- Lucas定理
- Lucas定理
- Lucas 定理。。。
- lucas定理
- Lucas定理
- lucas定理
- Lucas定理
- 2017年第0届浙江工业大学之江学院程序设计竞赛决赛—B qwb与矩阵
- Java实现-中序遍历和后续遍历构建二叉树
- Ubuntu16.04下基于GeForce1060显卡配置tensorflow-gpu
- Https完全协议详解
- spring 中常用的两种事务配置方式以及事务的传播性、隔离级别
- lucas定理
- 观察者模式与装饰者模式相遇的那些事
- ASP.Net中控件的EnableViewState属性
- Java中JNI的应用解析
- 使用 Spring Data JPA 简化 JPA 开发
- 40 C语言的两套标准
- JSON和JSONP的区别
- 找真正的入口(OEP)--广义ESP定律
- Spring事务配置的五种方式