Algorithm: CRT、EX-CRT & Lucas、Ex-Lucas
中国剩余定理
中国剩余定理,Chinese Remainder Theorem,又称孙子定理,给出了一元线性同余方程组的有解判定条件,并用构造法给出了通解的具体形式。
&现在有方程组:\\
&(S):\begin{cases}
x\equiv a_1(mod\space m_1)\\
x\equiv a_2(mod\space m_2)\\
\space\space\space\space. \\
\space\space\space\space. \\
\space\space\space\space. \\
x\equiv a_n(mod\space m_n)\\
\end{cases}\\
&中国剩余定理指出:若\forall i,j,1\le i<j\le n,i,j\in Z,都有gcd(m_i,m_j)=1,\\
&则对任意整数:a_1,a_2,...,a_n,方程组(S)均有解,且通解可以用如下方法构造。\\
&设M=\prod_{i=1}^n m_i,并设M_i={M\over m_i},\forall i\in\{1,2,...,n\},\\
&设t_i=M_i^{-1}(mod\space m_i),则方程通解为:x=kM+\sum_{i=1}^n a_it_iM_i,k\in Z。\\
\end{aligned}
\]
下面给出解的正确性证明:
\]
扩展中国剩余定理
在一般情况下,要求任两个数互质这个条件太苛刻了,CRT派不上用场,我们需要一个更具普遍性的结论,这就是EX-CRT。虽然是称为EX-CRT,但这个定理并没有直接用到CRT的结论。
&我们单独考虑方程组的前两个式子组成的同余方程组S':\\
&\begin{cases}
x\equiv a_1(mod\space m_1)\\
x\equiv a_2(mod\space m_2)\\
\end{cases}\\
&我们设整数K_1,K_2满足x=K_1m_1+a_1,x=K_2m_2+a_2,那么有K_1m_1+a_1=K_2m_2+a_2,\\
&即m_1K_1-m_2K_2=a_2-a_1,根据EXGCD,方程在gcd(m_1,m_2)|(a_2-a_1)时有解\\
&把由EXGCD求得的特解记为k_1,k_2,设S’的一个解为x_0,那么有:\\
&\begin{cases}
x_0=a_1+k_1\times{a_2-a_1\over gcd(m_1,m_2)}\times m_1\\
x_0=a_2+k_2\times{a_2-a_1\over gcd(m_1,m_2)}\times m_2\\
\end{cases}\\
&这样我们就得到了S'的一个解x_0,怎么求出它的通解呢?\\
\end{aligned}
\]
\]
&由解的关系有x_2=x_1+k\cdot lcm(m_1,m_2),k\in Z,结合之前得到的特解x_0,可以得出S'的通解X:\\
&X=x_0+k\cdot lcm(m_1,m_2),k\in Z\\
&上式又可以写成X\equiv x_0(mod\space lcm(m_1,m_2)),这样,我们就把S'转化为了一条同余式\\
&对于n条同余式,只需逐条合并即可。
\end{aligned}
\]
代码如下:
typedef long long ll;
const int maxn = 111;
// m为模数组,a为余数数组,0~n-1
ll m[maxn], a[maxn];
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1; y = 0;
return a;
}
ll ans = exgcd(b, a % b, y, x);
y -= a / b * x;
return ans;
}
ll excrt() {
ll lcm = m[0], last_a = a[0];
for(int i = 1; i < n; i++) {
ll lcm_a = ((a[i] - last_a) % m[i] + m[i]) % m[i];
ll k = lcm, x, y;
ll gcd = exgcd(lcm, m[i], x, y);
ll mod = m[i] / gcd;
x = (x * lcm_a / gcd % mod + mod) % mod;
lcm = lcm / gcd * m[i], last_a = (last_a + k * x) % lcm;
}
return (last_a % lcm + lcm) % lcm;
}
卢卡斯定理
卢卡斯定理是关于组合数和同余的定理,它表明当p为素数时:
&C_n^m=\prod_{i=0}^k C_{n_i}^{m_i}(mod\space p)\\
&其中,m=m_kp^k+m_{k-1}p^{k-1}+...+m_1p+m_0\\
&n=n_kp^k+n_{k-1}p^{k-1}+...+n_1p+n_0\\
&即m_i,n_i为m,n的p进制展开中对应次数为i的系数\\
\end{aligned}
\]
因为当m>n时,二项式系数为0,那么二项式系数即组合数能被p整除等价于在p进制下,存在某一位m的数值大于对应的n的数值。
基于母函数可以简单证明这个定理。
&∵无论p是质数还是合数,对\forall i\in N,0<i<p,都有C_p^i={p!\over i!(p-i)!}\\
&∴在母函数中,对于素数p,(1+x)^p\equiv1+x^p(mod\space p)\\
&运用数学归纳法可以得到,对\forall i\in N,(1+x)^{p^i} \equiv1+x^{p^i} (mod\space p)\\
&又对\forall t\in N以及素数p,把t表示为p进制数有:t=\sum_{i=0}^kt_ip^i\\
&那么,把m,n都用p进制表示,有\\
&\space\space\space\space\sum_{m=0}^n\left(C_n^m\cdot x^m\right)\\
&=(1+x)^n=\prod_{i=0}^k\left[(1+x)^{p^i}\right]^{m_i}\\
&\equiv\prod_{i=0}^k\left(1+x^{p^i}\right)^{m_i}
=\prod_{i=0}^k\left(\sum_{m_i=0}^{n_i}C_{n_i}^{m_i}\cdot x^{m_ip^i}\right)\\
&=\prod_{i=0}^k\left(\sum_{m_i=0}^{p-1}C_{n_i}^{m_i}\cdot x^{m_ip^i}\right)
=\sum_{m=0}^n\left[\left(\prod_{i=0}^kC_{n_i}^{m_i}\right)\cdot x^m\right] (mod\space p)
\end{aligned}
\]
代码如下:
typedef long long ll;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 100;
void init() {
F[0] = 1;
for(int i = 2; i < maxn; i++)
F[i] = i * F[i - 1] % mod;
}
ll qpow(ll a, ll b) {
ll ans = 1;
while(b) {
if(b & 1) ans = ans * a % mod;
b >>= 1; a = a * a % mod;
}
return ans;
}
ll lucas(ll N, ll M) {
ll ans = 1;
while(N & M) {
ll n = N % mod, m = M % mod;
if(n < m) return 0;
ans = ans * F[a] % mod * qpow(F[m] * F[n - m] % mod, mod - 2) % mod;
N /= p; M /= p;
}
return ans;
}
扩展卢卡斯定理
卢卡斯定理同样不能处理模数不是素数的情况,这时便需要扩展卢卡斯定理。我们一步步分析如何求解模数不是素数的组合数问题。
代码和推导部分参考了这篇博客。
&首先,我们要解决的问题是求C_n^m\%p,其中p不一定是素数。\\
&对于非素数,我们首先会联想到质因分解后结合CRT解决问题。\\
&假设分解得到t个质数,质数p_i对应的个数为k_i,对p质因分解有p=\prod_{i=1}^tp_i^{k_i}。\\
&显然对\forall i,j,1\le i<j\le t,gcd(p_i^{k_i},p_j^{k_j})=1,假设对\forall i\in[1,t],我们求出了a_i=C_n^m\%p_i^{k_i},\\
&那么我们可以得到同余方程组S:\\
&S:\begin{cases}
C_n^m\equiv a_1(mod\space p_1^{k_1})\\
C_n^m\equiv a_2(mod\space p_2^{k_2})\\
\space\space\space\space\space\space\space.\\
\space\space\space\space\space\space\space.\\
\space\space\space\space\space\space\space.\\
C_n^m\equiv a_t(mod\space p_t^{k_t})\\
\end{cases}\\
&这时我们便可以套用CRT解决问题,那么问题便转化为如何求解C_n^m\%p_i^{k_i}。
\end{aligned}
\]
&现在我们要求的是C_n^m\%p^k,其中p是素数。\\
&又C_n^m={n!\over m!(n-m)!},显然需要求出m!和(n-m)!关于模p^k的逆元,\\
&但考虑到这些项中可能包含p(含有p则不互质,逆元不存在),所以需要先提取p,\\
&得到:C_n^m={{n!\over p^{k_n}}\over{m!\over p^{k_m}}\cdot{(n-m)!\over p^{k_{n-m}}}}\times p^{k_n-k_m-k_{n-m}},这里的阶乘是指提取之后的结果。\\
&这时就可以计算{m!\over p^{k_m}}和{(n-m)!\over p^{k_{n-m}}}关于p^k的逆元了。\\
&这里,为了形式的统一,同时提取了n!中的p。那么,问题又转化为了如何求n!\%p^k。
\end{aligned}
\]
&目标:计算n!\%p^k,p为质数。上一步中提到,我们需要先提取p。\\
&提取结果为:n!=p^{\lfloor{n\over p}\rfloor}\times {\lfloor{n\over p}\rfloor}!\times \prod_{i=1,i\%p\ne 0}^ni。\\
&第一部分很好理解,对于每一个p的倍数,都可以提取出一个p,一共有{\lfloor{n\over p}\rfloor}个;\\
&第二部分为p的倍数被提取p之后余下的,是一个阶乘的形式。显然在n!中,\\
&对于p的幂,p的个数不止1个,也就是说第二部分仍然需要提取,这一部分可以递归解决。\\
&第三部分是n!剔除了p的倍数之后余下的。\\
&∵对\forall i<p^k,t\in N,都有i\equiv(i+t\cdot p^k)(mod\space p^k)\\
&∴对\forall t\in N,\prod_{i=1,i\%p\ne 0}^{p^k}i\equiv\prod_{i=1,i\%p\ne 0}^{p^k}(i+t\cdot p^k)(mod\space p^k)\\
&这也就是说,第三部分其实是存在循环节的,\prod_{i=1,i\%p\ne 0}^{p^k}i一共循环了\lfloor{n\over p^k}\rfloor次。\\
&除去循环节的余项长度在p^k之内,直接累乘即可。\\
\end{aligned}
\]
完整代码如下:
typedef long long ll;
const int N = 1e6 + 100;
ll n, m, p;
ll qpow(ll a, ll b, ll mod) {
ll ans = 1;
while(b) {
if(b & 1) ans = ans * a % mod;
b >>= 1; a = a * a % mod;
}
return ans;
}
ll fac(ll n, ll p, ll pk) {
if (!n) return 1;
ll ans = 1;
for (int i = 1; i < pk; i++)
if (i % p) ans = ans * i % pk;
ans = qpow(ans, n / pk, pk);
int npk = n % pk;
for (int i = 1; i <= npk; i++)
if (i % p) ans = ans * i % pk;
return ans * fac(n / p, p, pk) % pk;
}
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1; y = 0;
return a;
}
ll ans = exgcd(b, a % b, y, x);
y -= a / b * x;
return ans;
}
ll inv(ll a, ll p) {
if(!a) return 0;
int x, y;
exgcd(a, p, x, y);
return (x % p + p) % p;
}
ll C(ll n, ll m, ll p, ll pk) {
if (n < m) return 0;
ll fn = fac(n, p, pk),
fm = fac(m, p, pk),
fn_m = fac(n - m, p, pk),
cnt = 0;
for (ll i = n; i; i /= p)
cnt += i / p;
for (ll i = m; i; i /= p)
cnt -= i / p;
for (ll i = n - m; i; i /= p)
cnt -= i / p;
return fn * inv(fm * fn_m % pk, pk) % pk * qpow(p, cnt, pk) % pk;
}
ll a[N], mod[N]; // a[]是通过卢卡斯分解出来的组合数值,m[]是对应的模数
int cnt; // 质因数的种数
ll CRT() {
ll M = 1, ans = 0;
for (int i = 0; i < cnt; i++)
M *= mod[i];
for (int i = 0; i < cnt; i++)
ans = (ans + a[i] * (M / mod[i]) % M * inv(M / mod[i], mod[i]) % M) % M;
return ans;
}
ll exlucas(ll n, ll m, ll p) {
ll sqrtp = sqrt(p + 0.5);
for (int i = 2; p > 1 && i <= sqrtp; i++) {
ll pk = 1;
while (p % i == 0)
p /= i, pk *= i;
if (pk > 1)
a[cnt] = C(n, m, i, pk), mod[cnt++] = pk;
}
if (p > 1)
a[cnt] = C(n, m, p, p), mod[cnt++] = p;
return CRT();
}
Algorithm: CRT、EX-CRT & Lucas、Ex-Lucas的更多相关文章
- Algorithm: GCD、EXGCD、Inverse Element
数论基础 数论是纯数学的一个研究分支,主要研究整数的性质.初等数论包括整除理论.同余理论.连分数理论.这一篇主要记录的是同余相关的基础知识. 取模 取模是一种运算,本质就是带余除法,运算结果就是余数. ...
- 基础篇:java.security框架之签名、加密、摘要及证书
前言 和前端进行数据交互时或者和第三方商家对接时,需要对隐私数据进行加密.单向加密,对称加密,非对称加密,其对应的算法也各式各样.java提供了统一的框架来规范(java.security)安全加密这 ...
- 《Visual C++ 2010入门教程》系列四:VC2010中初学者常见错误、警告和问题
<Visual C++ 2010入门教程>系列四:VC2010中初学者常见错误.警告和问题 这一章将帮助大家解释一些常见的错误.警告和问题,帮助大家去理解和解决一些常见问题,并了解它的 ...
- 工程师技术(三):独立Web站点的快速部署、虚拟Web主机的部署、配置网页内容访问、使用自定Web根目录、配置安全Web服务、部署并测试WSGI站点
一.独立Web站点的快速部署 目标: 本例要求为 http://server0.example.com 配置Web站点,要求如下: 1> 从http://classroom/pub/materi ...
- 循序渐进nginx(三):日志管理、http限流、https配置,http_rewrite模块,第三方模块安装,结语
目录 日志管理 access_log error_log 日志文件切割 自定义错误页 http访问限流 限制请求数 语法 使用 限制连接数 语法 测试 补充: https配置 使用 生成证书 配置ng ...
- 《github一天,一个算术题》:堆算法接口(堆排序、堆插入和堆垛机最大的价值,并删除)
阅览.认为.编写代码! /********************************************* * copyright@hustyangju * blog: http://blo ...
- Mysql系列六:(Mycat分片路由原理、Mycat常用分片规则及对应源码介绍)
一.Mycat分片路由原理 我们先来看下面的一个SQL在Mycat里面是如何执行的: , ); 有3个分片dn1,dn2,dn3, id=5000001这条数据在dn2上,id=10000001这条数 ...
- Redis源码解析:27集群(三)主从复制、故障转移
一:主从复制 在集群中,为了保证集群的健壮性,通常设置一部分集群节点为主节点,另一部分集群节点为这些主节点的从节点.一般情况下,需要保证每个主节点至少有一个从节点. 集群初始化时,每个集群节点都是以独 ...
- 计图(Jittor) 1.1版本:新增骨干网络、JIT功能升级、支持多卡训练
计图(Jittor) 1.1版本:新增骨干网络.JIT功能升级.支持多卡训练 深度学习框架-计图(Jittor),Jittor的新版本V1.1上线了.主要变化包括: 增加了大量骨干网络的支持,增强了辅 ...
- atitit.管理学三大定律:彼得原理、墨菲定律、帕金森定律
atitit.管理学三大定律:彼得原理.墨菲定律.帕金森定律 彼得原理(The Peter Principle) 1 彼得原理解决方案1 帕金森定律 2 如何理解墨菲定律2 彼得原理(The Pete ...
随机推荐
- JeeSite | 保存信息修改记录
需求点 在很多场景中信息是不能轻易被修改的,修改时要么需要具备权限,要么需要审批,但是无论是哪种方式,修改前后的数据都是需要留有“案底”的,也就是说关键的信息被修改后是有修改记录的,一般修改记录会记录 ...
- HTTP常见的几种认证机制
几种常用的认证机制 ===================转自https://www.cnblogs.com/xiekeli/红心李的文章====================== 我是一个测试人员 ...
- 图解servlet
You can see the following illustration to better understand the lifecycle of the Servlet. When the r ...
- NGINX 配置清单
以下内容来自 SimulatedGREG/nginx-cheatsheet. 通用设置 端口 listen server { # standard HTTP protocol listen 80; # ...
- python网络爬虫进阶之HTTP原理,爬虫的基本原理,Cookies和代理介绍
目录 一.HTTP基本原理 (一)URI和URL (二)超文本 (三)HTTP和HTTPS (四)HTTP请求过程 (五)请求 1.请求方法 2.请求的网址 3.请求头 4.请求体 (六)响应 1.响 ...
- C++ 流插入"<<"和流提取">>"运算符的重载
01 流插入<<运算符的重载 C++ 在输出内容时,最常用的方式: std::cout << 1 <<"hello"; 问题: 那这条语句为什么 ...
- 备战双十一,腾讯WeTest有高招——小程序质量优化必读
WeTest 导读 2018年双十一战场小程序购物通道表现不俗,已逐渐成为各大品牌方角逐的新战场.数据显示,截止目前95%的电商平台都已经上线了小程序.除了电商企业外,许多传统线下商家也开始重视小程序 ...
- Apache2 在Linux环境下的安装
安装Apache2: apt-get install apache2 启动Apache2服务: service apache2 start 在终端运行启动后,打开浏览器URL访问 http://loc ...
- 3.智能快递柜(通信篇-HTTP)
1.智能快递柜(开篇) 2.智能快递柜(终端篇) 3.智能快递柜(通信篇-HTTP) 4.智能快递柜(通信篇-SOCKET) 5.智能快递柜(通信篇-Server程序) 6.智能快递柜(平台篇) 7. ...
- ucoreOS_lab 1~8 实验报告导航
所有的实验已经全部完成,实验的源代码及报告都在 Github 上,欢迎大家批评指正,如果觉得对你有帮助的话,欢迎为此项目 star & watch & fork 三连,让更多的朋友们看 ...