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;}

另一种

现在目标是求Cmn%pCnm%p,p为素数(经典p=1e9+7)

虽然有Cmn=n!m!(nm)!Cnm=n!m!(n−m)!,但由于取模的性质对于除法不适用,所以Cmn%pCnm%p(n!%pm!%p(nm)!%p)%p(n!%pm!%p∗(n−m)!%p)%p

所以需要把“除法”转换成“乘法”,才能借助取模的性质在不爆long long的情况下计算组合数。这时候就需要用到“逆元”!

  逆元:对于a和p,若a*b%p≡1,则称b为a%p的逆元。

那这个逆元有什么用呢?试想一下求(ab)(ab)%p,如果你知道b%p的逆元是c,那么就可以转变成(ab)(ab)%p = a*c%p = (a%p)(c%p)%p

那怎么求逆元呢?这时候就要引入强大的费马小定理!

  费马小定理:对于a和素数p,满足$a^{p-1}$%p≡1

接着因为ap1ap−1 = ap2aap−2∗a,所以有ap2aap−2∗a%p≡1!对比逆元的定义可得,ap2ap−2是a的逆元!

所以问题就转换成求解ap2ap−2,即变成求快速幂的问题了(当然这需要满足p为素数)。

现在总结一下求解Cmn%pCnm%p的步骤:

  1. 通过循环,预先算好所有小于max_number的阶乘(%p)的结果,存到fac[max_number]里 (fac[i] = i!%p)
  2. 求m!%p的逆元(即求fac[m]的逆元):根据费马小定理,x%p的逆元为xp2xp−2,因此通过快速幂,求解fac[m]p2fac[m]p−2%p,记为M
  3. 求(n-m)!%p的逆元:同理为求解fac[nm]p2fac[n−m]p−2%p,记为NM
  4. Cmn%pCnm%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);      }  } 



原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 狗狗得了耳螨怎么办 狗狗得耳螨怎么办 狗狗的耳螨怎么办 人被传染了耳螨怎么办 人被传染耳螨怎么办 头发干枯毛躁怎么办做什么发型 学校不给转学籍怎么办 结婚证号码错了怎么办 继教学分1类不够怎么办 猫咪耳螨传染人怎么办 ph值低了怎么办呀 水的ph值高怎么办 宝宝起热痱子了怎么办 宝宝热的起痱子怎么办 吃了有农药的菜怎么办 水银吃到肚子里怎么办 牙齿有牙结石怎么办可以去除 刷牙牙结石掉了怎么办 痘痘留下的黑印怎么办 脸上用了激素药怎么办 激素药膏用多了怎么办 8岁儿童牙齿不齐怎么办 两个牙齿之间有缝隙怎么办 蛀牙就剩牙根了怎么办 牙龈发白像烂了怎么办 3岁宝宝牙龈肿痛怎么办 1岁宝宝牙龈肿痛怎么办 牙疼引起的发烧怎么办 2岁小儿牙龈红肿怎么办 宝宝出牙牙龈红肿怎么办 3岁宝宝牙龈红肿怎么办 1岁宝宝牙龈红肿怎么办 4岁宝宝牙龈红肿怎么办 儿童牙黑了怎么办啊 牙黑了掉了一块怎么办 1岁幼儿牙齿腐蚀怎么办 我的大牙变黑了怎么办 牙齿里面黑了疼怎么办 最里面的牙黑了怎么办 牙龈的肉裂开了怎么办 有蛀牙怎么办可以变好么