扩展中国剩余定理
扩展中国剩余定理
使用场景
跟中国剩余定理相同,假设现在有多个同余线性方程,要你求解满足线性方程的最小正整数解,但是不保证每一个方程组的模数互质。
这时候我们就不能简单地用中国剩余定理直接计算了,思考一下,假如前k个,我们找到了答案x,想要继续满足第k+1个,我们需要让x不断地加上前k个模数的lcm,直到他满足新条件,显然,我们用枚举去求解时间复杂度过高,此时我们就可以用扩展欧几里得来快速的计算出来这个答案。
来看式子
为了不破坏前面已经满足的条件,我们只能在 nowx 的基础上,加上若干次 nowm。假设这个“若干次”是 \(k\) 次,那么新的数字就是:\(nowx + k \cdot nowm\)。为了让它满足新条件,它必须满足:$$nowx + k \cdot nowm \equiv b_i \pmod{a_i}$$我们把同余符号扒掉,变成等式(假设商是 \(y\)):$$nowx + k \cdot nowm = b_i + y \cdot a_i$$移项整理一下,把未知数 \(k\) 和 \(y\) 留在一边:$$nowm \cdot k - a_i \cdot y = b_i - nowx$$你看!这句话翻译成数学语言,完完全全就是一个标准形态的二元一次不定方程 \(Ax + By = C\)!其中我们需要求的,就是这个 \(k\)(也就是你代码里的 x)。
然后我就用exgcd来求解出一个满足的最小解x1,然后把他乘上右边的数除以gcd(a,b),这样的话就可以,计算出一个合法解,然后我们再把这个数变成一个正数,再把它带回我原本的式子,就可以计算出来一个最小的合法答案
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
i64 n,g,m,p,t;
i64 a[300005],b[300005];
const i64 mod1=999911659;
i64 ksm(i64 a,i64 b,i64 mod){
i64 res=1;
while(b){
if(b&1)res=res*a%mod;
a=a*a%mod;
b=b>>1;
}
return res;
}
i64 gcd(i64 a,i64 b){
if(b==0)return a;
return gcd(b,a%b);
}
i64 lcm(i64 a,i64 b){
if(a==0||b==0)
return max(a,b);
return a/gcd(a,b)*b;
}
void exgcd(i64 a,i64 b,i64& x,i64& y){
if(b==0){
x=1;
y=0;
return;
}
i64 x1,y2;
exgcd(b,a%b,x1,y2);
x=y2;
y=x1-a/b*y2;
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i];
}
i64 nowm=0,nowx=0;
i64 x,y;
nowx=b[1];
nowm=a[1];
for(int i=2;i<=n;i++){
exgcd(a[i],nowm,x,y);//求解一个最小x
i64 h=nowm/gcd(nowm,a[i]);
x=(i64)((x*__int128(nowx-b[i])/gcd(nowm,a[i]))%h+h)%h;//求解一个最小合法解x
nowm=lcm(nowm,a[i]);//扩大一下最小公倍数
nowx=((__int128)x*a[i]+b[i])%nowm;//带入原本的式子,求出一个最小正数解
}
cout<<nowx<<'\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
solve();
}

浙公网安备 33010602011771号