1.当n,m都很小的时候可以利用杨辉三角直接求。
C(n,m)=C(n-1,m)+C(n-1,m-1);

2、n和m较大,但是p为素数的时候

Lucas定理是用来求 c(n,m) mod p,p为素数的值

C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p

也就是Lucas(n,m)%p=Lucas(n/p,m/p)*C(n%p,m%p)%p

求上式的时候,Lucas递归出口为m=0时返回1

求C(n%p, m%p)%p的时候,此处写成C(n, m)%p(p是素数,n和m均小于p)

C(n, m)%p = n! / (m ! * (n - m )!) % p = n! * mod_inverse[m! * (n - m)!, p] % p

由于p是素数,有费马小定理可知,m! * (n - m)! 关于p的逆元就是m! * (n - m)!的p-2次方。

p较小的时候预处理出1-p内所有阶乘%p的值,然后用快速幂求出逆元,就可以求出解。p较大的时候只能逐项求出分母和分子模上p的值,然后通过快速幂求逆元求解。

P较大,不打表:

 ll pow(ll a, ll b, ll m)
{
ll ans = ;
a %= m;
while(b)
{
if(b & )ans = (ans % m) * (a % m) % m;
b /= ;
a = (a % m) * (a % m) % m;
}
ans %= m;
return ans;
}
ll inv(ll x, ll p)//x关于p的逆元,p为素数
{
return pow(x, p - , p);
}
ll C(ll n, ll m, ll p)//组合数C(n, m) % p
{
if(m > n)return ;
ll up = , down = ;//分子分母;
for(int i = n - m + ; i <= n; i++)up = up * i % p;
for(int i = ; i <= m; i++)down = down * i % p;
return up * inv(down, p) % p;
}
ll Lucas(ll n, ll m, ll p)
{
if(m == )return ;
return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
}

P较小,打表:

 const int maxn = 1e5 + ;
ll fac[maxn];//阶乘打表
void init(ll p)//此处的p应该小于1e5,这样Lucas定理才适用
{
fac[] = ;
for(int i = ; i <= p; i++)
fac[i] = fac[i - ] * i % p;
}
ll pow(ll a, ll b, ll m)
{
ll ans = ;
a %= m;
while(b)
{
if(b & )ans = (ans % m) * (a % m) % m;
b /= ;
a = (a % m) * (a % m) % m;
}
ans %= m;
return ans;
}
ll inv(ll x, ll p)//x关于p的逆元,p为素数
{
return pow(x, p - , p);
}
ll C(ll n, ll m, ll p)//组合数C(n, m) % p
{
if(m > n)return ;
return fac[n] * inv(fac[m] * fac[n - m], p) % p;
}
ll Lucas(ll n, ll m, ll p)
{
if(m == )return ;
return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
}

3、n,m较大且p不为素数的时候

扩展Lucas定理:

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + ;
const int mod = 1e9 + ;
ll pow(ll a, ll b, ll m)
{
ll ans = ;
a %= m;
while(b)
{
if(b & )ans = (ans % m) * (a % m) % m;
b /= ;
a = (a % m) * (a % m) % m;
}
ans %= m;
return ans;
}
ll extgcd(ll a, ll b, ll& x, ll& y)
//求解ax+by=gcd(a, b)
//返回值为gcd(a, b)
{
ll d = a;
if(b)
{
d = extgcd(b, a % b, y, x);
y -= (a / b) * x;
}
else x = , y = ;
return d;
}
ll mod_inverse(ll a, ll m)
//求解a关于模上m的逆元
//返回-1表示逆元不存在
{
ll x, y;
ll d = extgcd(a, m, x, y);
return d == ? (m + x % m) % m : -;
} ll Mul(ll n, ll pi, ll pk)//计算n! mod pk的部分值 pk为pi的ki次方
//算出的答案不包括pi的幂的那一部分
{
if(!n)return ;
ll ans = ;
if(n / pk)
{
for(ll i = ; i <= pk; i++) //求出循环节乘积
if(i % pi)ans = ans * i % pk;
ans = pow(ans, n / pk, pk); //循环节次数为n / pk
}
for(ll i = ; i <= n % pk; i++)
if(i % pi)ans = ans * i % pk;
return ans * Mul(n / pi, pi, pk) % pk;//递归求解
} ll C(ll n, ll m, ll p, ll pi, ll pk)//计算组合数C(n, m) mod pk的值 pk为pi的ki次方
{
if(m > n)return ;
ll a = Mul(n, pi, pk), b = Mul(m, pi, pk), c = Mul(n - m, pi, pk);
ll k = , ans;//k为pi的幂值
for(ll i = n; i; i /= pi)k += i / pi;
for(ll i = m; i; i /= pi)k -= i / pi;
for(ll i = n - m; i; i /= pi)k -= i / pi;
ans = a * mod_inverse(b, pk) % pk * mod_inverse(c, pk) % pk * pow(pi, k, pk) % pk;//ans就是n! mod pk的值
ans = ans * (p / pk) % p * mod_inverse(p / pk, pk) % p;//此时用剩余定理合并解
return ans;
} ll Lucas(ll n, ll m, ll p)
{
ll x = p;
ll ans = ;
for(ll i = ; i <= p; i++)
{
if(x % i == )
{
ll pk = ;
while(x % i == )pk *= i, x /= i;
ans = (ans + C(n, m, p, i, pk)) % p;
}
}
return ans;
} int main()
{
ll n, m, p;
while(cin >> n >> m >> p)
{
cout<<Lucas(n, m, p)<<endl;
}
return ;
}

组合数取模方法总结(Lucas定理介绍)的更多相关文章

  1. 2015 ICL, Finals, Div. 1 Ceizenpok’s formula(组合数取模,扩展lucas定理)

    J. Ceizenpok’s formula time limit per test 2 seconds memory limit per test 256 megabytes input stand ...

  2. 组合数取模介绍----Lucas定理介绍

    转载https://www.cnblogs.com/fzl194/p/9095177.html 组合数取模方法总结(Lucas定理介绍) 1.当n,m都很小的时候可以利用杨辉三角直接求. C(n,m) ...

  3. CF451E Devu and Flowers (隔板法 容斥原理 Lucas定理 求逆元)

    Codeforces Round #258 (Div. 2) Devu and Flowers E. Devu and Flowers time limit per test 4 seconds me ...

  4. 【Gym 100947E】Qwerty78 Trip(组合数取模/费马小定理)

    从(1,1)到(n,m),每次向右或向下走一步,,不能经过(x,y),求走的方案数取模.可以经过(x,y)则相当于m+n步里面选n步必须向下走,方案数为 C((m−1)+(n−1),n−1) 再考虑其 ...

  5. Codeforces 57C (1-n递增方案数,组合数取模,lucas)

    这个题相当于求从1-n的递增方案数,为C(2*n-1,n); 取模要用lucas定理,附上代码: #include<bits/stdc++.h> using namespace std; ...

  6. [UOJ 275/BZOJ4737] 【清华集训2016】组合数问题 (LUCAS定理的运用+数位DP)

    题面 传送门:UOJ Solution 这题的数位DP好蛋疼啊qwq 好吧,我们说回正题. 首先,我们先回忆一下LUCAS定理: \(C_n^m \equiv C_{n/p}^{m/p} \times ...

  7. BZOJ 3782: 上学路线 [Lucas定理 DP]

    3782: 上学路线 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 192  Solved: 75[Submit][Status][Discuss] ...

  8. [BZOJ4591][SHOI2015]超能粒子炮·改(Lucas定理+数位DP)

    大组合数取模可以想到Lucas,考虑Lucas的意义,实际上是把数看成P进制计算. 于是问题变成求1~k的所有2333进制数上每一位数的组合数之积. 数位DP,f[i][0/1]表示从高到低第i位,这 ...

  9. 组合数取模&&Lucas定理题集

    题集链接: https://cn.vjudge.net/contest/231988 解题之前请先了解组合数取模和Lucas定理 A : FZU-2020  输出组合数C(n, m) mod p (1 ...

随机推荐

  1. Golang之并发资源竞争(读写锁)

    前面的有篇文章在讲资源竞争的时候,提到了互斥锁.互斥锁的根本就是当一个goroutine访问的时候,其他goroutine都不能访问,这样肯定保证了资源的同步,避免了竞争,不过也降低了性能. 仔细剖析 ...

  2. amazeui笔记-web组件

    Json.parse()

  3. Cheatsheet: 2018 11.01 ~ 2019 02.28

    Golang FromXToGo micro - A microservice toolkit Other Easy parsing of Excel spreadsheet format with ...

  4. Guava学习笔记之Maps(1):Maps.uniqueIndex(Iterable, Function)

    Guava官方文档 https://github.com/google/guava/wiki/CollectionUtilitiesExplained 官方文档这样描述: [`Maps.uniqueI ...

  5. 后台数据校验-BeanCheck

    package com.ldf.domain; import java.text.ParseException; public class UserCheck { //从表单获取的数据 private ...

  6. MyBatis学习(一)---配置文件,Mapper接口和动态SQL

    MyBatis MyBatis官方学习网站 http://www.mybatis.org/mybatis-3/zh/index.html 为什么需要MyBatis? Jdbc操作数据库的不足之处 1. ...

  7. C#学习笔记(基础知识回顾)之值类型与引用类型转换(装箱和拆箱)

    一:值类型和引用类型的含义参考前一篇文章 C#学习笔记(基础知识回顾)之值类型和引用类型 1.1,C#数据类型分为在栈上分配内存的值类型和在托管堆上分配内存的引用类型.如果int只不过是栈上的一个4字 ...

  8. webpack-bundle.js原理

    bundle.js 源码 //a.js import { log } from './b.js' log('hello') //b.js export const log = function (m) ...

  9. 在IE、fixfox、chrome等浏览器中ajax提交成功后,打开新标签页面被浏览器拦截问题[转]

    如题: 在项目中要在当前页面中,再新开一个页面, 新开页面的地址是ajax请求后返回的url --------- 试了,浏览器提示组织弹窗..... 网上找,找到了一个处理方式,思路是 1. 先打开一 ...

  10. number to string

    C++进行int to string和string to int 下面方法一存在内存泄露 #include<strstream>void main(){ std::strstream ss ...