快速阶乘与(扩展)卢卡斯定理

\(p\)为质数时

考虑 \(n!~mod~p\) 的性质

当\(n>>p\)时,不妨将\(n!\)中的因子\(p\)提出来

\(n!\) 可以写成 \(a*p^e\) , \(a\)与\(p\)互质

如何求解\(a\)和\(e\)?

显然,\(e=n/p+n/p^2+n/p^3+……\)

因为\(1\)~\(n\)有\(n/p\)个\(p\)的倍数,贡献为\(1\),\(n/p^2\)个\(p^2\)的倍数,贡献为\(2\)……

事实上,可以每次先将\(1\)$n$中$p$的倍数的因子提出来,$p,2p,3p...(n/p)p$就变成了$1,2,3...n/p$,$e+=n/p$,而$1$\(n/p\)这些数的\(a\)和\(e\)又可以递归求解

再考虑\(a\)的求法:

将\(1\)$n$中$p$的倍数拿走,递归处理求得$1$ \(n/p\)的\(a\),再乘上剩下的数\(1,2,3...p-1,p+1,p+2,...2p-1,2p+1,2p+2,...\)就行了

而剩下的数在对\(p\)取模意义下是从\(1\)~\(p-1\)的循环,于是可以预处理\(n< p\)时的\(n!\)

剩下的数的积即为: \(((p-1)!)^{n/p}\times(n~mod~p)!\)

根据威尔逊定理,\((p-1)!\equiv -1 ~(mod~p)\),可以省去快速幂

递归求解即可

卢卡斯定理

#include<iostream>
#include<cstring>
#include<cstdio>
#define int long long
using namespace std; const int MAXN=100010; int fact[MAXN]; inline int qpow(int x,int k,int p){
int s=1%p;
while(k){
if(k&1) s=s*x%p;
k>>=1;
x=x*x%p;
}
return s;
} inline int mod_fact(int n,int &e,int p){
e=0;
if(n<p) return fact[n];
int res=mod_fact(n/p,e,p);
e+=n/p;
if(n/p%2!=0) return res*(p-fact[n%p])%p; //((p-1)!)^(n/p)=(-1)^(n/p)
return res*fact[n%p]%p;
} inline int C(int n,int m,int p){
if(m>n) return 0;
int e1,e2,e3;
int a1=mod_fact(n,e1,p),a2=mod_fact(m,e2,p),a3=mod_fact(n-m,e3,p);
if(e1>e2+e3) return 0; //p^(e1-e2-e3)是p的倍数,return 0
return a1*qpow(a2*a3%p,p-2,p)%p;
} int n,m,p; signed main()
{
int T;
scanf("%lld",&T);
while(T--){
scanf("%lld%lld%lld",&n,&m,&p);
fact[0]=1;
for(int i=1;i<=p;++i) //预处理阶乘
fact[i]=fact[i-1]*i%p;
printf("%lld\n",C(n+m,m,p));
}
return 0;
}

\(p\)为任意数时

考虑将\(p\)分解为\(p_1^{a_1}p_2^{a_2}p_3^{a_3}...p_k^{a_k}\)

分别求解\(C(n,m)~mod ~ p_i^{a_i}\)

然后用\(CRT\)合并就可以了

求解\(C(n,m)~mod~p_i^{a_i}\)时,仍然递归求解\(1\)~\(n/p_i\),但是\(a\)是对\(p_i^{a_i}\)取模,而非对\(p_i\)取模

非\(p_i\)的倍数的数乘积计算与上面也有不同,首先\(p_i^{a_i}\)是合数,逆元只能用\(exgcd\)求了

因为是对\(p_i^{a_i}\)取模,循环是\(p_i^{a_i}-a_i\),而且不能用威尔逊定理,需要先从\(1\)~\(p_i^{a_i}\)暴力乘,再用快速幂直接计算,剩下的再暴力乘进去即可,此时预处理\(fact[i]\)就不必要了

扩展卢卡斯

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define int long long
using namespace std; const int MAXN=1000010; inline int qpow(int x,int k,int p){
int s=1;
while(k){
if(k&1) s=s*x%p;
k>>=1;
x=x*x%p;
}
return s;
} inline int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1; y=0;
return a;
}
int t=exgcd(b,a%b,y,x);
y-=a/b*x;
return t;
} inline int mod_fact(int n,int &e,int p,int MOD){
if(!n) return 1;
int res=1;
for(int i=2;i<=MOD;++i) //暴力累乘一个循环
if(i%p) res=res*i%MOD;
res=qpow(res,n/MOD,MOD); //共有n/MOD个循环
e+=n/p;
for(int i=2;i<=n%MOD;++i)
if(i%p) res=res*i%MOD; //乘上循环外面的数
return res*mod_fact(n/p,e,p,MOD)%MOD; //递归处理n/p个p的倍数
} inline int C(int n,int m,int p,int MOD){
if(m>n) return 0;
int e1=0,e2=0,e3=0;
int a1=mod_fact(n,e1,p,MOD),a2=mod_fact(m,e2,p,MOD),a3=mod_fact(n-m,e3,p,MOD);
if(e1-e2-e3>0){
int temp=pow(p,e1-e2-e3);
if(temp>=MOD) return 0; //p^(e1-e2-e3)是p^ai的倍数
a1=a1*temp%MOD;
}
int x,y; exgcd(a2*a3%MOD,MOD,x,y);
return a1*x%MOD;
} int n,m,p,a[110],b[110],cnt; signed main()
{
scanf("%lld%lld%lld",&n,&m,&p);
int k=sqrt(p),x=p;
for(int i=2;i<=k;++i)
if(x%i==0){
int t=1;
while(x%i==0)
x/=i,t*=i;
a[++cnt]=t;
b[cnt]=C(n,m,i,t);
}
if(x!=1){
a[++cnt]=x;
b[cnt]=C(n,m,x,x);
}
int A=a[1],B=b[1];
for(int i=2;i<=cnt;++i){
int k1,k2;
int g=exgcd(A,a[i],k1,k2);
int t=A;
A=A*a[i]/g;
B=(t*k1%A*(b[i]-B)/g%A+B)%A;
}
printf("%lld\n",(B+A)%A);
return 0;
}

【luoguP4720】【模板】扩展卢卡斯的更多相关文章

  1. [洛谷P4720] [模板] 扩展卢卡斯

    题目传送门 求组合数的时候,如果模数p是质数,可以用卢卡斯定理解决. 但是卢卡斯定理仅仅适用于p是质数的情况. 当p不是质数的时候,我们就需要用扩展卢卡斯求解. 实际上,扩展卢卡斯=快速幂+快速乘+e ...

  2. 洛谷P4720 【模板】扩展卢卡斯

    P4720 [模板]扩展卢卡斯 题目背景 这是一道模板题. 题目描述 求 C(n,m)%P 其中 C 为组合数. 输入输出格式 输入格式: 一行三个整数 n,m,p ,含义由题所述. 输出格式: 一行 ...

  3. 洛谷 P4720 【模板】扩展 / 卢卡斯 模板题

    扩展卢卡斯定理 : https://www.luogu.org/problemnew/show/P4720 卢卡斯定理:https://www.luogu.org/problemnew/show/P3 ...

  4. P4720【模板】扩展卢卡斯,P2183 礼物

    扩展卢卡斯定理 最近光做模板了 想了解卢卡斯定理的去这里,那题也有我的题解 然而这题和卢卡斯定理并没有太大关系(雾 但是,首先要会的是中国剩余定理和exgcd 卢卡斯定理用于求\(n,m\)大,但模数 ...

  5. LG4720 【模板】扩展卢卡斯定理

    扩展卢卡斯定理 求 \(C_n^m \bmod{p}\),其中 \(C\) 为组合数. \(1≤m≤n≤10^{18},2≤p≤1000000\) ,不保证 \(p\) 是质数. Fading的题解 ...

  6. bzoj2142 礼物——扩展卢卡斯定理

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2142 前几天学了扩展卢卡斯定理,今天来磕模板! 这道题式子挺好推的(连我都自己推出来了) , ...

  7. 【知识总结】扩展卢卡斯定理(exLucas)

    扩展卢卡斯定理用于求如下式子(其中\(p\)不一定是质数): \[C_n^m\ mod\ p\] 我们将这个问题由总体到局部地分为三个层次解决. 层次一:原问题 首先对\(p\)进行质因数分解: \[ ...

  8. VS自定义项目模板:[2]创建VSIX项目模板扩展

    VS自定义项目模板:[2]创建VSIX项目模板扩展 听语音 | 浏览:1237 | 更新:2015-01-02 09:21 | 标签:软件开发 1 2 3 4 5 6 7 分步阅读 一键约师傅 百度师 ...

  9. 【Luogu3807】【模板】卢卡斯定理(数论)

    题目描述 给定\(n,m,p(1≤n,m,p≤10^5)\) 求 \(C_{n+m}^m mod p\) 保证\(P\)为\(prime\) \(C\)表示组合数. 一个测试点内包含多组数据. 输入输 ...

随机推荐

  1. c#中泛型

    整理一下昨天学习的泛型,有不对的地方欢迎指正: 泛型类 定义一个类,这个类中某些字段的类型不确定,这些类型可以在构造类时确定下来 2.泛型方法 泛型方法就是定义一个方法,这个方法的参数类型可以是不确定 ...

  2. tensorflow中使用变量作用域及tf.variable(),tf,getvariable()与tf.variable_scope()的用法

    一 .tf.variable() 在模型中每次调用都会重建变量,使其存储相同变量而消耗内存,如: def repeat_value(): weight=tf.variable(tf.random_no ...

  3. PAT 1008数组元素右移问题

    PAT 1008数组元素右移问题 一个数组A中存有N(>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(≥0)个位置,即将A中的数据由(A​0​​A​1​​⋯A​N−1​​)变 ...

  4. 【转载】C#中ArrayList集合类使用RemoveAt方法移除指定索引的元素

    ArrayList集合是C#中的一个非泛型的集合类,是弱数据类型的集合类,可以使用ArrayList集合变量来存储集合元素信息,任何数据类型的变量都可加入到同一个ArrayList集合中,在Array ...

  5. 编写可维护的JavaScript-随笔(七)

    将配置数据从代码中分离出来 代码中有些数据有修改的可能,如果放在函数中的话后期修改的时候会带来一些不必要的风险 需要将配置数据从代码中抽取出来,如果配置数据多的话可以放入一个对象中,然后修改抽取出来的 ...

  6. 渗透 Facebook 的思路与发现

    0x00 写在故事之前 身一位渗透测试人员,比起 Client Side 的弱点,我更喜欢 Server Side 的攻击,能够直接控制服务器并获得权限操作 SHELL 才爽 . 当然一次完美的渗透出 ...

  7. 08-Vuex

    Vuex 一.简介 ① 是什么:是一个状态管理工具,存放项目组件中的公共数据 二.使用语法 ① 语法 -1. 创建 Vuex 实例 const store = new Vuex.Store({ sta ...

  8. HttpUtils请求工具类

    package com.cmcc.hybj.payment.framework.https; import java.io.UnsupportedEncodingException;import ja ...

  9. Git拉取Gitlab上的代码时,报128的解决方法

    今天拉取gitlab上的代码时出现错误,一直返回128 首先我们确定我们在存储库上有没有权限,然后我就去项目中的 Members上看是否有权限,然后发现也是有的. 然后克隆的时候发现输入一万遍密码都还 ...

  10. JVM 运行时数据区:程序计数器、Java 虚拟机栈和本地方法栈,方法区、堆以及直接内存

    Java 虚拟机可以看作一台抽象的计算机,如同真实的计算机,它也有自己的指令集和运行时内存区域. Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存(运行时内存区域)划分为若干个不同的数 ...