洛谷 P4777 【模板】扩展中国剩余定理(EXCRT)
传送门
拓展中国剩余定理EXCRT
早知道有这东西就不学CRT了呜呜呜(ljCRT)
这个比CRT范围更广更快更好写……
虽然我写挂了,并且调了两天四五个小时
还是这一堆式子:
\[\begin{cases}
x\equiv b_1\pmod {a_1}\\
x\equiv b_2\pmod {a_2}\\
x\equiv b_3\pmod {a_3}\\
\cdots\cdots\cdots\cdots\\
\end{cases}\]
但是 \(a_i\) 不需要互质了。
首先考虑通解:
假设我们已经求出了某个同余方程的一个特解 \(x\),那么通解就等于 \(x+ka\)。
于是我们就有了一个思路——不断合并同余方程。
那么问题来了,怎么合并两个同余方程式?
先求出其中一个的通解,设为 \(ans+xa_1\),则有:
\[ans+xa_1\equiv b_2\pmod {a_2}
\]
移项得:
\[xa_1\equiv b_2-ans\pmod {a_2}
\]
这个就可以转化成:
\[xa_1+ya_2=b_2-ans
\]
再使用 \(exgcd\) 求得 \(x\) 的一个解,新的特解就等于 \(ans+xa_1\)。
接下来就把这个特解继续带入下一个同余方程式,一步步计算即可。
注意要使用龟速乘。
注意事项
主要问题在龟速乘上,请看龟速乘。
AC代码
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=100005;
int n;
long long m,x,y,ans;
long long a[maxn],b[maxn];
inline long long ksc(long long a,long long b,long long mod){
return (a*b-(long long)((long double)a/mod*b)*mod+mod)%mod;
}
long long exgcd(long long a,long long b){
if(b==0){
x=1;
y=0;
return a;
}
long long gcd=exgcd(b,a%b);
swap(x,y);
y=y-(a/b)*x;
return gcd;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) scanf("%lld%lld",&a[i],&b[i]);
m=a[1],ans=b[1]%a[1];
for(int i=2;i<=n;i++){
long long gcd=exgcd(m%a[i],a[i]);
long long c=b[i]-ans;
x=ksc(x,c/gcd,a[i]);
long long M=m/gcd*a[i];
ans=((ans+x*m)%M+M)%M;
m=M;
}
cout<<(ans%m+m)%m;
return 0;
}