【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;
}
posted @ 2020-05-13 21:57  Nanjo  阅读(135)  评论(0编辑  收藏  举报