LG4777 【模板】扩展中国剩余定理(EXCRT)
题意
题目描述
给定\(n\)组非负整数\(a_i, b_i\),求解关于\(x\)的方程组
\[\begin{cases} x \equiv b_1\ ({\rm mod}\ a_1) \\ x\equiv b_2\ ({\rm mod}\ a_2) \\ ... \\ x \equiv b_n\ ({\rm mod}\ a_n)\end{cases}$$的最小非负整数解。
## 输入输出格式
### 输入格式:
输入第一行包含整数$n$。
接下来$n$行,每行两个非负整数$a_i, b_i$。
### 输出格式:
输出一行,为满足条件的最小非负整数$x$。
## 输入输出样例
### 输入样例#1:
3
11 6
25 9
33 17
### 输出样例#1:
809
## 说明
$n \leq 10^5, 1 \leq a_i \leq 10^{12}, 0 \leq b_i \leq 10^{12}, b_i < a_i$,保证答案不超过$10^{18}$。
请注意程序运行过程中进行乘法运算时结果可能有溢出的风险。
数据保证有解
# [niiick](https://www.luogu.org/blog/niiick/solution-p4777)的题解
问题
求解同余方程组
\]
\left{\begin{aligned}x\equiv\ a_1(\mod m_1) \quad\ x\equiv\ a_2(\mod m_2) \quad\ x\equiv\ a_3(\mod m_3) \quad\ ...\quad\x\equiv\ a_k(\mod m_k) \quad\end{aligned}\right.
\[其中$m_1,m_2,m_3...m_k$为不一定两两互质的整数, 求x的最小非负整数解
求解
假设已经求出前k-1个方程组成的同余方程组的一个解为x
且有$M=\prod_{i-1}^{k-1}m_i$(补充:代码实现中用的就是$M=LCM_{i-1}^{k-1}m_i$,显然易证这样是对的,还更能防止溢出,上述是为了先方便理解
则前k-1个方程的方程组通解为$x+i*M(i\in Z)$
那么对于加入第k个方程后的方程组
我们就是要求一个正整数t,使得$x+t*M \equiv a_k(\mod m_k)$
转化一下上述式子得$t*M \equiv a_k-x(\mod m_k)$
对于这个式子我们已经可以通过扩展欧几里得求解t
若该同余式无解,则整个方程组无解, 若有,则前k个同余式组成的方程组的一个解解为$x_k=x+t*M$
所以整个算法的思路就是求解k次扩展欧几里得
```cpp
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;
rg char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') w=-1;
ch=getchar();
}
while(isdigit(ch))
data=data*10+ch-'0',ch=getchar();
return data*w;
}
template<class T>il T read(rg T&x){
return x=read<T>();
}
typedef long long ll;
co int maxn=1e5+1;
int n;
ll ai[maxn],bi[maxn];
ll mul(ll a,ll b,ll mod){
ll re=0;
while(b){
if(b&1) re=(re+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return re;
}
ll exgcd(ll a,ll b,ll&x,ll&y){
if(b==0) {x=1,y=0;return a;}
ll gcd=exgcd(b,a%b,x,y);
ll z=x;x=y;y=z-y*(a/b);
return gcd;
}
ll excrt(){
ll x,y;
ll M=bi[1],ans=ai[1];
for(int i=2;i<=n;++i){
ll a=M,b=bi[i],c=(ai[i]-ans%b+b)%b;
ll gcd=exgcd(a,b,x,y),bg=b/gcd;
if(c%gcd) return -1;
x=mul(x,c/gcd,bg);
ans+=x*M;
M*=bg;
ans=(ans%M+M)%M;
}
return ans;
}
int main(){
// freopen(".in","r",stdin),freopen(".out","w",stdout);
read(n);
for(int i=1;i<=n;++i) read(bi[i]),read(ai[i]);
printf("%lld\n",excrt());
return 0;
}
```\]
静渊以有谋,疏通而知事。