数论
todo
- 威尔逊定理 原根 二次剩余,这些另开文章
- 积性函数与筛子相关
- 数论分块
目录
-
前置知识与符号定义
-
素数筛
-
裴蜀定理
-
(扩展)欧几里得算法
-
同余方程
-
费马小定理
-
(扩展)欧拉定理
-
模意义下的乘法逆元
-
(扩展)卢卡斯定理
-
(扩展)中国剩余定理
0.前置知识与符号定义
在无特殊说明的情况下,所有数字均 \(\in \mathbb{Z}\),且一般 \(\in\mathbb{Z^+}\)。
0.0 唯一分解定理
对于任意正整数 \(t\) 有唯一分解:
其中 \(\forall p\) 是互不相同的素数。
这个定理的意思是:每一个正整数都可以分解成若干不同素数的乘积。
0.1. 素数
对于一个数 \(p\),\(p \ne 0,\pm 1\),且 \(p\) 没有除 \(1\) 与 \(p\) 以外的因数,则认为 \(p\) 是素数,否则认为 \(p\) 是合数。
0.2. 最大公因数
定义 \(\gcd(a,b)\) 为 \(a,b\) 两数的最大公因数,最大公因数是指同时是 \(a,b\) 的因数的数。
设两数分别为 \(a,b\),我们会发现最大公因数的一个等价定义:
由于:
我们有:
\(\forall p\) 仍是互不相同的质数。
\(\operatorname{lcm}\) 可以同理定义。
0.3. 整除符号
定义 \(d|a\) 表示 \(a\) 可被 \(d\) 整除。
因为我在给 whk 的同学讲的时候手误把定义敲反了,所以一个等价定义是:
若 \(d|a\),则 \(a \bmod d = 0\),即 \(a=dq,q\in \mathbb{Z}\)。
0.4. 模运算
定义 \(a \bmod b=a-\lfloor \frac{a}{b} \rfloor \times b\)。
特殊的,当输出一个数 \(k\) 对 \(p\) 取模的结果时,请使用
cout<<(k%p+p)%p而非cout<<k%p,这可能导致一些符号错误。
有以下性质:
证明显然。
见“模意义下的乘法逆元”
0.5. 同余
若 \(a\bmod m =b\bmod m\),则称 \(a,b\) 同余,即 \(a \equiv b \pmod m\)。
有以下性质:
这是同余的一个重要基本性质。
由以上性质,有以下三个推论:
值得注意的是,等式的基本性质中加减乘三个运算一般在同余中成立,而除法需要特殊讨论,如下式。
若 \(\gcd (c,m)=1\),则:
证明:
\[\begin{aligned} &\because ac \equiv bc \pmod m \\ &\therefore m|(ac-bc) \\ &\Rightarrow m|c(a-b) \\ &\because \gcd(c,m)=1 \\ &\therefore m|(a-b) \\ &\Rightarrow a\equiv b \pmod m \end{aligned} \]
并且,若 \(d=\gcd (a,m)\) 我们有推论:
证明:
\[\begin{aligned} &\because ac \equiv bc \pmod m \\ &\therefore m |c(a-b) \end{aligned} \]若有整数 \(k\) 使得 \((a-b)c=mk\),则
\[\begin{aligned} & \because m |c(a-b) \\ & \therefore \dfrac{(a-b)c}{d}=\dfrac{mk}{d} \\ & \because \gcd(\frac{c}{d},\frac{m}{d})=1 \\ & \therefore \dfrac{m}{d}|(a-b) \\ & \therefore a \equiv b \pmod {\frac{m}{d}} \end{aligned} \]
这是部分题目中的重要性质。
0.6. 快速幂
和数论没一点关系,放个板子。
https://www.luogu.com.cn/problem/P1226
对于快速幂的问题定义如下:给定两个整数 \(a,b\),求 \(a^b \mod k\) 。
对于此类问题,暴力时间复杂度 \(O(n)\) ,考虑优化。
我们不妨将指数采用二进制表述,设指数的二进制串为 \(S_{i}\),\(tmp\) 表示当前二进制位对答案的贡献,\(a\) 表示底数。
若 \(S_{k}=1\),则 \(ans \gets ans \times tmp\)。由于第 \(k\) 位对答案的贡献为\(a^{2^{k-1}} \times a^{2^{k-1}}\),即 \(a^{tmp} \times a^{tmp}\),化简得 \(a^{tmp^2}\) 所以我们在每次操作后使 \(tmp \gets tmp \times tmp\) 即可。
其时间复杂度为 \(O(\log n)\),迭代常数较小。
代码:
int m;
int qpow(int a,int b)
{
int ans=1,tmp=a;
while(b!=0)
{
if(b&1)
{
ans=(ans%m*tmp%m)%m;
}
tmp=tmp*tmp%m;
b>>=1;
}
return ans;
}
0.7. 两道奇妙例题
-
对于 \(n\in\mathbb{Z}\),证明 \(\dfrac{(n^2)!}{(n!)^{n+1}}\in\mathbb{Z}\)。
组合意义保平安!
考虑以下问题情景,将 \(n^2\) 个人分成 \(n\) 组,每组 \(n\) 人,求方案总数。
显然将 \(n^2\) 个人的全排列的方案数为 \((n^2)!\),而在每组内不需要考虑元素的先后顺序,所以多算了 \((n!)^n\) 种方案。而这 \(n\) 组本身也不需要考虑先后顺序,所以又多算了 \(n!\) 种方案。
所以这个问题的答案即为 \(\dfrac{(n^2)!}{(n!)^{n+1}}\),显然为整数,证毕。
-
对于 \(n\in\mathbb{Z}\),证明 \(n^5\equiv n\pmod {10}\)。
鸽巢原理的奇特应用。
等价于证明 \(10|(n^5-n)\),因式分解可得 \(n(n-1)(n+1)(n^2+1)\)。
变换形式,得\(n(n-1)(n+1)(n+2)(n+3)-(5n+5)n(n-1)(n+1)=n(n-1)(n+1)(n+2)(n+3)-5n(n-1)(n+1)^2\)。
由鸽巢原理,注意到以下两条基本性质:
- 连续的 \(5\) 个数必定是 \(10\) 的倍数。
- 连续的 \(2\) 个数必定是 \(2\) 的倍数。
由此,设 \(n(n-1)(n+1)(n+2)(n+3)=10p,n(n-1)=2q\)。
上式即为 \(10p-5(n+1)^2\times 2q\)。
化简得 \(10(p-(n+1)^2q)\),显然是 \(10\) 的倍数,证毕。
1. 素数筛
注:也可以筛一些其他东西。
1.1. 埃氏筛
对于素数筛问题的模板题
埃氏筛的思想如下,若我们需要筛出小于等于 \(n\) 的素数,可以枚举 \(2\) 至 \(n\) ,若当前枚举的数 \(k\) 是一个素数,则我们将 \(k\) 的倍数都标志为合数。特殊的,我们不去关心小于 \(k\) 的 \(k\) 倍的数,因为该数在被 \(k\) 考虑之前一定被小于 \(k\) 的数考虑过。
可以证明,其时间复杂度为 \(O(n \log \log n)\)。
以下代码实现筛出 \(n\) 以内的质数:
void prime(int n)
{
int t=0;
for(int i=2;i<=n;i++)
isprime[i]=1;
for(int i=2;i<=n;i++)
{
if(isprime[i])
{
prime[++t]=i;
if(i*i<=n)
for(int j=i*i;j<=n;j+=i)
isprime[j]=0;
}
}
}
1.2.欧拉筛
欧拉筛模板题。
埃氏筛的时间复杂度为 \(O(n \log \log n)\),无法通过本题。
欧拉筛的时间复杂度为 \(O(n)\)。
对于欧拉筛,每一个数会且仅会被筛到一次。
其做法是对于一个数 \(k\),遍历当前质数表 \(S\)(\(S\) 呈单调递增),若 \(k \mod S_{i}=0\) 则停止遍历。
容易证明其正确性,若 \(k\mod S_{i}=0\),则说明 \(S_{i}\) 是 \(k\) 的最小质因数。即 \(S_{i+1}\) 一定不是其最小质因数。那么 \(i\) 乘上 \(S_{i+a}\) 的结果一定会被 \(S_{i}\) 的倍数筛到。
附代码:
void prime(int n)
{
int t=0;
for(int i=2;i<=n;i++)
{
if(!chck[i])
pri[++t]=i;
for(int j=1;i*pri[j]<=n&&j<=t;j++)
{
chck[i*pri[j]]=1;
if(i%pri[j]==0)
break;
}
}
}
1.3. Miller Rabin
原理:基于费马小定理
给定若干组 \(a\),考虑上式是否成立。一般的,我们只需要取比 \(p\) 小的素数测试即可。
若 \(p\) 是一个奇素数,则有:
证明:
综合以上定理,可以找到 \(k\) 个特殊的素数 \(p\) 代入公式进行验证即可判断素数,方法如下:
-
令 \(p-1=m*2^k\),计算 \(q_0=p^m\),若 \(q_0= 1\),则通过。
-
否则设 \(q_i=q^{2}_{i-1}\),若 \(q_i=1 \operatorname{and} q_{i-1} \neq 1 \operatorname{or} p-1\),则一定是合数。
-
若 \(q_k\) 不为 \(1\) 则为合数。
-
否则进行下一组测试。
-
所有测试通过即为素数。
bool miller_rubin(ll a,ll n)
{
ll s=n-1,r=0;
while((s&1)==0){
s>>=1;r++;
}
ll k=qmod(a,s,n);
if(k==1) return true;
for(int i=0;i<r;i++,k=k*k%n){
if(k==n-1) return true;
}
return false;
}
bool is_prime(ll n)
{
ll times =_;
ll prime[]={_};
for(int i=0;i<times;i++){
//miller_rubin();
}
}
2. 裴蜀定理
裴蜀定理的描述如下:
对于整数 \(a,b\)(\(a,b\) 中至少有一个不为 \(0\)),方程 \(ax+by=\gcd(a,b)\) 一定有解。
有以下推论:
-
对于整数 \(a,b\)(\(a,b\) 中至少有一个不为 \(0\)),方程 \(ax+by=d\) ,则 \(d=\gcd(a,b)\)(逆定理)。
-
对于一个数列 \(a\)(\(a\) 中元素不全为 \(0\)), \(\sum^{n}_{i=1} a_{i}x_{i}=\gcd^n_{i=1}a_{i}\) 一定有解。
下文给出了一个构造性证明。
3. 扩展欧几里得算法(exgcd)
3.1. 辗转相除法(欧几里得算法)
辗转相除法可以用来求解最大公因数等问题。
其主要算法流程如下:
-
给定两数 \(a,b\),我们认为 \(a>b\)。
-
若 \(b=0\) 返回 \(a\)。否则递归求解 \(\gcd(b,a \mod b)\)
算法正确性证明如下:
首先证明 \(\gcd(a,b)\) 的答案是 \(gcd(b,a \mod b)\) 的答案。
我们设 \(a=bk+c\),则 \(\gcd(a,a \mod b)=c\),设 \(d|a\) 且 \(d|b\) ,则有 \(c=a-bk\),\(\dfrac{c}{d}=\dfrac{a}{d}-\dfrac{b}{d}k\)。
不难发现 \(\dfrac{c}{d}\) 是整数,即 \(d|c\),所以 \(\gcd(a,b)\) 的答案是 \(gcd(b,a \mod b)\) 的答案。
逆命题容易证明。
Q.E.D
代码如下:
int gcd(int a,int b)
{
if(b==0) return a;
else return gcd(b,a%b);
}
对于两整数 \(a,b\),有:
可以从唯一分解定理的角度进行证明,即:
由此,有:
int lcm(int a,int b)
{
return a*b/gcd(a,b);
}
使用 <algorithm> 库中的 __gcd() 函数也可。
注意 <numeric> 库中的 std::gcd 和 std::lcm 仅在 C++ 17 及以上标准中可用。
3.2 扩展欧几里得算法
3.2.1. exgcd 求不定方程特解
扩展欧几里得算法(exgcd)常用于这样一类问题:给定一个不定方程 \(ax+by=c\),求其一组合法的整数解,即裴蜀定理。
即:求 \(ax+by=\gcd(a,b)\),并将求得的 \(x,y\) 分别乘以 \(\dfrac{c}{\gcd(a,b)}\)。
我们可以尝试将问题转化为 4.1 中的欧几里得算法问题,即为 exgcd 算法。
与欧几里得法相似的,我们可以将 \(ax+by=\gcd(a,b)\) 递归为 \(bx'+(a \bmod b)y' = \gcd(b,a \bmod b)\)
特殊的,exgcd 中 \(ax+by=\gcd(a,b)\) 与 \(bx'+(a \bmod b)y' = \gcd(b,a \bmod b)\) 并不等价。
由欧几里得算法:
假设 \(bx'+(a \bmod b)y ' = \gcd(a,b)\) 中,\(x',y'\) 已知,由模运算定义:
所以
由定义,\(\gcd(a,0)=a\),此时 \(x=1,y=0\)。
所以我们只需要递归到 \(b=0\) 时终止即可。
于是我们求出了不定方程 \(ax+by=c\) 的一组整数解,这也间接地证明了裴蜀定理。
附代码:
int exgcd(int a,int b,int &x,int &y) {
if (b == 0) {
x = 1, y = 0;
return a;
}
int h = exgcd(b, a % b, y, x);
y -= a / b * x;
return h;
}
3.2.2. exgcd 求不定方程通解
假设我们已经知道了不定方程 \(ax+by=c\) 的一组解 \(x_{0},y_{0}\),若 \(x_{0}\) 增加 \(r\) 则方程左边增加 \(ar\),为保证等式成立,\(by\) 需减去 \(ar\),同时,\(y\) 是整数。这意味着 \(ar\) 是 \(b\) 的倍数。
我们不妨设 \(t=ar\),因为 \(ar\) 是 \(b\) 的倍数,所以最小的 \(t=\operatorname{lcm}(a,b)\)。
由最小公倍数性质:
带回原式,得:
\(y\) 需减去:
所以,不定方程 \(ax+by=c\) 的通解是:
3.2.3 不定方程的无解情况
这与裴蜀定理所讨论的情况不同。
对于不定方程 \(ax+by=c\) 其有解的必要条件是 \(c \bmod \gcd(a,b) = 0\)
证明如下:由最大公因数定义,\(a,b\) 都是 \(\gcd(a,b)\) 的倍数,所以 \(ax+by\) 是 \(\gcd(a,b)\) 的倍数,即 \(c\) 是 \(\gcd(a,b)\) 的倍数。若 \(c \bmod \gcd(a,b) \ne 0\),与 \(c\) 是 \(\gcd(a,b)\) 的倍数矛盾,不成立。此时不定方程无解。
4. 同余方程
同余方程模板题。
同余方程指的是形如 \(ax \equiv 1 \pmod b\) 的方程。
对于这种方程,我们可以将其转化为上文的不定方程 \(ax+by=c\)。
为什么?
\(a \equiv b \pmod p\) 的本质是 \(a\) 与 \(b\) 之间相差了若干个 \(p\)。
所以 \(ax \equiv 1 \pmod b \iff ax+by \equiv 1\)
特殊的,与不定方程类似,同余方程同样可能无解,这发生在 \(a,p\) 不互质的情况,证明如下:
对于不定方程 \(ax+by=1\),其有解的条件是 \(1 \bmod \gcd(a,b)=0\),即 \(\gcd(a,b) =1\)。
5. 费马小定理
费马小定理的描述如下:若 \(p\) 为素数,\(\gcd(a,p)=1\),则 \(a^{p-1}\equiv 1 \pmod p\)
费马小定理是欧拉定理的一个推论,将在下文证明其正确性。
一个简单地应用:若 \(p\) 是素数,\(\gcd(a,p)=a\) ,则:
另一个不简单的应用是 Miller-Rabbin 算法。
6.欧拉定理
6.1 欧拉定理
定义 \(\varphi(i)\) 为 \(1 \sim n\) 中与 \(i\) 互质的数的个数。
欧拉定理如下:
若 \(\gcd(a,p)=1\),则有:
证明如下:
引理: \(r_{1},r_{2},\dots,r_{\varphi(p)}\) 为 \(p\) 的一个简化剩余系,且 \(\gcd(a,p)=1\) 则 \(ar_{1},ar_{2},\dots,ar_{\varphi(p)}\) 也为 \(p\) 的一个简化剩余系。
证明:\(\forall 1\le i\le \varphi(p)\),有 \(\gcd(a,r_{i})=\gcd(p,r_{i})=1\)。
所以 \(\gcd(ap,r_{i})=1\),即该元素仍存在于剩余系中。
原命题等价于:
两遍同时约掉 \(\prod_{i=1}^{\varphi(p)} r_i\),得:
Q.E.D.
6.2. 扩展欧拉定理
考虑没有对 \(\gcd(a,m)\) 限定的情况,不带证明的,给出:
结论背过就行。
7. 模意义下的乘法逆元

所以我们有必要先知道什么是模意义下的乘法逆元(本文不讨论其它逆元,下文逆元皆指“模意义下的乘法逆元”)。
给定整数 \(a,p\),若 \(ax \equiv 1\pmod p\),则称 \(x\) 是 \(a\) 的逆元。
如果抛弃模意义,乘法逆元其实就是取其倒数。
在下文中可能用 \(a^{-1}\) 表示 \(a\) 的逆元。
7.1. exgcd求逆元
逆元模板题。
显然可以用 4. 中提到的同余方程求解。
需要注意的是,由于不定方程可能无解,所以逆元只存在于 \(a,p\) 互质时。
值得注意的是,这里仅要求 \(a,p\) 互质,而费马小定理还要求 \(p\) 一定为质数。
代码:
void exgcd(int a, int b, int &x, int &y)
{
if (!b)
x=1,y=0;
else
exgcd(b,a%b,y,x),y-= a/b*x;
}
exgcd(a,p,x,y);//a 在 mod p 下的逆元为 x。
7.2. 费马小定理求逆元
由费马小定理,\(a^{p-1}\equiv 1 \pmod p\)。
而逆元要求的式子是 \(ax \equiv 1 \pmod p\)。
不难发现 \(x=a^{p-2}\),快速幂求出即可。
求一个数的逆元的时间复杂度 \(O(\log n)\),可以在模板题中拿到 64 pts。
7.3. 线性求逆元
如果我们需要求 \(1 \sim n\) 中所有数的逆元,时间复杂度为 \(O(n\log n)\),不可接受。
可以在线性时间复杂度求解该问题。
显然,对于任意的 \(p\),$1^{-1} \equiv 1\pmod p $
我们可以递归的求解 \(i^{-1}\)。
设 \(k=\lfloor \dfrac{p}{i} \rfloor,j=p \bmod i\),则:
将其代回模 \(p\) 意义下,得:
两遍同乘 \(i^{-1}j^{-1}\),得:
移项,得:
即:
我们可以发现,\((p \bmod i)^{-1}\) 一定会在 \(i^{-1}\) 之前求出,所以我们可以递推地求逆元。
特殊的,当 \(p\equiv 0 \pmod i\) 时 \(p \bmod i\) 未定义,这是由于在 \(p\) 和 \(i\) 不互质时,没有逆元导致的,所以我们一般会将 \(p\) 设置为大质数,以防止这种情况的发生,这也是部分数论题的模数为大质数的原因。
代码:
inv[1]=1;
for(int i=2;i<=n;i++)
{
inv[i]=(p-p/i)*inv[p%i]%p;
}
我们使用 p-p/i 来避免出现负数。
7.4. 线性求任意 \(n\) 个数逆元
时间复杂度要求 \(O(n)\)。
这与 7.3 没有关系。
设 \(s_{n}=\prod^{n}_{i=1}a_{i}\),并计算 \(s_{n}^{-1}\)。
显然,有 \(s_{i-1}^{-1}=s_{i}^{-1}a_{i}\),故可以递推计算 \(s_{i}^{-1}\)。
有 \(a_{i}^{-1}=s_{i}^{-1} \times s_{i-1}\)。原问题得解。
代码:
s[0]=1;
for(int i=1;i<=n;i++)
s[i]=s[i-1]*a[i]%p;
invs[n]=qpow(s[n],p-2);//请注意题目所给是否是质数
for(int i=n;i>=1;i--)
invs[i-1]=invs[i]*a[i]%p;
for(int i=1;i<=n;i++)
inva[i]=invs[i]*s[i-1]%p;
8.卢卡斯定理
8.1 模数为质数的情况
卢卡斯定理可以解决一些大组合数取模问题。
对于质数 \(p\),有:
在此不做证明。
值得注意的,\(\binom{\lfloor\frac{n}{p}\rfloor}{\lfloor\frac{m}{p}\rfloor}\) 可继续递归求解,直至 \(m=0\) 为止。
int solve(int n, int m) {
if (m == 0) {
return 1;
}
else {
return solve(n / p, m / p) % p * C(n % p, m % p) % p;
}
}
8.2 exLucas
需先阅读 9.1。
事实上,\(p\) 为小合数时,也有类似的做法,这被称为 exlucas,即求:
由于 \(p\) 是合数,我们不妨先使用 CRT/exCRT进行拆分,设 \(p=p_1^{\alpha_1}p_2^{\alpha_2}\cdots p_t^{\alpha_t}\),得:
问题转化为求解 \(C^m_n\bmod p^{k},p\in\texttt{prime}\)。
展开,得:
我们无法直接计算 \(m!(n-m)!\) 的逆元,但由于 \(p\) 是质数,所以可以变形为:
其中,\(x,y,z\) 指分子含有因数 \(p\) 的个数。
由于两数互质是存在逆元的充分必要条件,上式的分母一定存在模 \(p^{k}\) 意义下的逆元。
即我们将问题进一步转化为求:
在这个式子中,我们考虑 \(n!\) 是由哪几部分组成的。借用 oi-wiki 的例子,我们以 \(n=22,p=3,k=2\) 进行举例:
观察最后的这个式子,它由三部分组成:
- \(3\) 的幂,其次数是 \(\lfloor\frac{n}{p}\rfloor\)。
- \(\lfloor\frac{n}{p}\rfloor!\) 它和第一部分共同构成了与 \(p\) 互质部分的乘积。
- \(n!\) 中,与 \(p\) 不互质部分的乘积,首先是每 \(p^k\) 个一组,共 \(\lfloor\frac{n}{p}\rfloor\) 组,之后是无法被完整归到组内的剩余部分。
形式化的,\(n!\pmod {p^{k}}\Longleftrightarrow\\\)
由于 \(\lfloor\dfrac{n}{p}\rfloor\) 恰好等于 \(x\),将 \((5)\) 式变形,定义函数 \(f(n)\) 为:
由于 \(\lfloor\dfrac{n}{p}\rfloor\) 中还与 \(p^k\) 存在公因数,所以该部分可以递归求解,边界为 \(f(0)=1\)。
现在我们考虑如何求解 \((4)\) 式中的 \(x\)。
由于 \(x\) 恰好等于 \(\lfloor\dfrac{n}{p}\rfloor\),所以可定义函数 \(g(x)\) 为:
将 \((7)\) 式代回 \((3)\) 式,得:
将 \((3)\) 式的答案通过 CRT/exCRT 合并,即可得到 \((1)\) 式答案。
int f(int n, int mod, int k) {//k 底数 n当前分解 mod 完整的模数(p^k)
if (n == 0) {
return 1;
}
int ans = 1;
for (int i = 1; i <= mod; i++) {
if (i % k != 0) {
ans = ans * i % mod;
}
}
ans = qpow(ans, n / mod, mod);
for (int i = mod * (n / mod); i <= n; i++) {
if (i % k != 0) {
ans = ans * (i % mod) % mod;
}
}
ans = (ans * f(n / k, mod, k)) % mod;
return ans;
}
int g(int n, int k) {
if (n < k) {
return 0;
}
return g(n / k, k) + n / k;
}
int solve(int k, int mod) {
return f(n, mod, k) * inv(f(m, mod, k), mod) % mod * inv(f(n-m, mod, k), mod) % mod * qpow(k, g(n, k) - g(m, k) - g(n - m, k), mod) % mod;
}
int exlucas(int n, int m, int mod) {
int upp = sqrt(mod);
for (int i = 2; i <= upp; i++) {
if (mod % i == 0) {
++top;
pri[top] = i;
while (mod > 0 && mod % i == 0) {
pnum[top] ++;
mod /= i;
}
}
}
if (mod != 1) {
pri[++ top] = mod;
pnum[top] ++;
}
for (int i = 1; i <= top; i++) {
ans[i] = solve(pri[i], qpow(pri[i], pnum[i]));
}
int res = ans[1], resmod = qpow(pri[1], pnum[1]);
for (int i = 2; i <= top; i++) {
excrt(res, resmod, ans[i], qpow(pri[i], pnum[i]));
}
return res;
}
9.中国剩余定理(CRT)
9.1. 模数互质的情况
中国剩余定理可以求解如下的方程组的解,:
其中 \(p\) 两两互质。
我们可以构造地解出该方程组。
-
设 \(P=\prod_{i=1}^{k}p_{i}\)。
-
设 \(m_{i}=\dfrac{P}{p_{i}}\)
-
求解 \(m_{i}\) 在\(\pmod {p_{i}}\) 意义下的逆元 \(m_{i}^{-1}\)。
-
设\(c_{i}=m_{i}m_{i}^{-1}\)。
答案即为 \(\sum_{i=1}^{k} a_{i}c_{i} \pmod {P}\)。
证明如下:
设 \(i \ne j\),则:
又有:
代入原式,得:
故对于 \(\forall 1\le i\le k,x\equiv a_{i}\pmod {p_{i}}\)。
证毕!
CRT 代码:
int CRT()
{
int sump=0,ans=0;
for(int i=1;i<=n;i++)
sump*=p[i];
for(int i=1;i<=n;i++)
{
int b,y;
m[i]=sump/p[i];
exgcd(m[i],p[i],b,y);
invm[i]=b;
c[i]=invm[i]*m[i];
ans+=a[i]*c[i]%sump;
}
return (ans%sump+sump)%sump;
}
CRT 告诉了我们这样一件事,对于两两互质的数列 \(p_{1},p_{2},\cdots,p_{k}\) 中 \(P=0\sim \prod_{i=1}^{k}p_{i}\) 这 \(P\) 个数与 \([0,m_{1}),[0,m_2),\cdots,[0,m_k)\) 这样的 \(k\) 个二元组之间存在一一对应关系。
也就是说当我们想要求一个在 \(\prod_{i=1}^{k}p_i\) 之内的数时,我们只需要求其分别对 \(p_1,p_2,\cdots,p_k\) 取模的值,再使用 CRT 合并即可。
把 P 分解为质因数整数幂的成绩,对于每个质因数整数幂作为模数分别求解(这往往比任意合数好求),再用 CRT 合并,是数论题常见的解法——@zhqwq
这类问题的模板题:[SDOI2010] 古代猪文。
9.2. exCRT
求解:
的正整数解,其中 \(p\) 不保证两两互质。
考虑前两个方程,将其合并,我们有:
由裴蜀定理,当 \(\gcd(m_1,m_2)|(a_1+a_2)\) 时,该方程有解。
讨论有解情况时,可以将两边同时除以 \(\gcd(m1,m2)\),得:
得到 \(k_1p+k_2q=y\),由于 \(k1,k2\) 互质,所以我们可以得出一组它的解 \((u,i)\),然后有:
对于式 \((10)\),我们可以将式 \((12)\) 代入得到:
代入式 \((11)\) 得:
至此,我们完成了两个同余方程的合并,并得到了特解。容易发现,新的同余式子是在 \(\bmod \operatorname{lcm}(m1,m2)\) 意义下的。
因通解是容易求的,为:
我们不妨去想一想这是为什么,对 \(\operatorname{lcm}(m1,m2)\) 取模的结果,就是将整个整数集分成了 \(\operatorname{lcm}(m1,m2)\) 个等价类,如果等价类里有 \(1\) 个解,那肯定所有都是解。
一人得道,鸡犬升天。
——阮行止
按该方法进行合并即可。
void excrt(int &a, int &mod, int aa, int mmod) {
int g = __gcd(mod, mmod);
int m = mod / g * mmod;
int p, q;
exgcd(mod / g, mmod / g, p, q);
p = p * mod % m * (aa - a) / g % m;
a = (a + p + m) % m;
mod = m;
}
wrote on 23.11.3
upd on 23.11.28
upd on 23.12.1
upd on 24.1
upd on 24.9.1 这回应该真写完了。

浙公网安备 33010602011771号