【Luogu P4777】 扩展中国剩余定理(EXCRT)
声明:拓展CRT和CRT没什么关系
拓展CRT的做法:每次利用拓展欧几里得合并两条同余方程,最后就能得出结果。
\[\begin{cases} x\equiv b_1 \pmod {a_1}\\ x\equiv b_2 \pmod {a_2}\\\end{cases}
\]
对于这两条方程,我们分别转化成不定方程的形式:
\[\begin{cases}
x+a_1*k_1=b_1\\
x+a_2*k_2=b_2\\
\end{cases}
\]
显然我们需要做的就是解出符合题意的\(k_1,k_2\),考虑将两个式子作差:
\[a_1*k_1-a_2*k_2=b_1-b_2
\]
可以用EXGCD求解,解出了对应的\(k_1\)之后就可以构造出对应的\(x\),但是这个\(x\)是在模什么的意义下成立的呢?
事实上,为了同时满足最上面的两条方程,这个模数必须取\(lcm(a_1,a_2)\)的倍数。
简单证明一下:
\[\begin{cases}
x\equiv b_1 \pmod {a_1}\\
x\equiv b_2 \pmod {a_2}\\
\end{cases}
\]
假设借上面的方法求出来对应的解为\(c\),证明这个方程组等价于\(x\equiv c \pmod {lcm(a_1,a_2)}\)
将这个方程化为\(x=c+k*lcm(a_1,a_2)\),很容易发现一点,对于符合这种形式的x,第二项对\(a_1,a_2\)取模一定为\(0\)。然后根据我们上面的解答过程,可以发现\(c\)对\(a_1,a_2\)取模一定分别为\(b_1,b_2\)。
#include<cstdio>
#include<algorithm>
#define int long long
#define ll long long
#define ld long double
#define ull unsigned long long
using namespace std;
int n,a[100005],b[100005];
int exgcd(int a,int b,int &x,int &y)
{
if (b==0)
{
x=1,y=0;
return a;
}
int ans=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return ans;
}
inline ll ksc(ll x,ll y,ll p)
{
ll z=(ld)x/p*y;
ll res=(ull)x*y-(ull)z*p;
return (res+p)%p;
}//快速乘,防炸ll
signed main()
{
scanf("%lld",&n);
for (int i=1;i<=n;i++)
scanf("%lld%lld",&a[i],&b[i]);
int GCD,A1,A2,K1,K2,R,x=b[1],LCM;
for (int i=2;i<=n;i++)
{
if (b[i]<b[i-1]) swap(a[i],a[i-1]),swap(b[i],b[i-1]);
A1=a[i-1],K1,A2=a[i],K2,R=b[i]-b[i-1];
GCD=exgcd(A1,A2,K1,K2);LCM=A1/GCD*A2;
K1=(K1%A2+A2)%A2;
K1=ksc(K1,R/GCD,A2);
x=(ksc(K1,A1,LCM)+b[i-1])%LCM;
b[i]=x,a[i]=LCM;
}
printf("%lld",x);
return 0;
}