【模板】扩展中国剩余定理(EXCRT)
EXCRT的学习笔记。
其实感觉也没有阮行止说的那么复杂,感觉第二篇题解说得清楚一些,其实就是考虑怎么在求出前 i 个方程通解的基础上扩展出前 i+1 个方程的通解。
假如当前的通解是 \(x=tM+x_0\) ,有结论是 \(M=lcm(b_1,b_2\dots b_{i-1})\) ,这个可以感性理解。t是浮动变量,x0是前i-1个方程的特解,也就是说我们希望的是找到一个 \(x=tM+x_0\) ,满足 \(x\equiv a_i\pmod{b_i}\) 。推柿子:
\[tM+x_0\equiv a_i\pmod{b_i}
\]
\[tM+x_0=a_i-kb_i
\]
\[tM+b_ik=a_i-x_0
\]
首先用翡蜀定理判断是否有解,即判断 \(a_i-x_0\) 和 \(gcd(M,b_i)\) 的关系。如果有解就直接除掉gcd,然后用扩欧解方程
\[tM'+kb_i'=1
\]
解出来的 \(t_0\) 要做两步处理。首先模上 \(b_i'\) ,然后乘上 \((a_i-x_0)/gcd\) ,这样我们就得到了一个特解 \(t_0\) 。但别忘了我们的 \(x=tM+x_0\) ,所以要更新答案,并且更新 \(M\) 即可。
#include<bits/stdc++.h>
//#define feyn
#define int long long
#define ll __int128
const int N=100010;
using namespace std;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
inline int mul(int s1,int s2,int mod){
return (int)((ll)s1*(ll)s2%(ll)mod);
}
struct node{int x,y,g;};
inline node exgcd(int a,int b){
if(b==0)return (node){1,0,a};
node an=exgcd(b,a%b);
return (node){an.y,an.x-a/b*an.y,an.g};
}
int m,a[N],b[N];
signed main(){
#ifdef feyn
freopen("in.txt","r",stdin);
#endif
read(m);
for(int i=1;i<=m;i++){
read(b[i]);read(a[i]);
}
int M=b[1],x0=a[1];
for(int i=2;i<=m;i++){
node an=exgcd(M,b[i]);
int c=a[i]-x0,bg=b[i]/an.g;
int x=mul(an.x,c/an.g,bg);
x0=(x0+x*M);
M=M*bg;
}
printf("%lld",(x0%M+M)%M);
return 0;
}
一如既往,万事胜意