EXCRT 其实和 CRT 半毛钱关系都没有……
我们在 CRT 中需要用到求逆元的操作:
inv(biM,bi)
但是如果 bi 不满足两两互质,我们就无法求出逆元,这样 CRT 就用不了了。
为了解决这个问题,求出 bi 不一定两两互质时同余方程组
⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧x≡a1(modb1)x≡a2(modb2)x≡a3(modb3)……x≡an(modbn)
的解,EXCRT 就诞生了。
首先考虑两个方程的情况:
{x≡a1(modb1)x≡a2(modb2)
可以转化为这样:
{x=a1+k1⋅b1x=a2+k2⋅b2
即:
a1+k1⋅b1=a2+k2⋅b2
移项:
k1⋅b1−k2⋅b2=a2−a1
这个柿子很明显能使用 exgcd 求解。
很明显,若 a2−a1modgcd(b1,b2)=0 那么无解。
设 y1,y2 满足 y1⋅b1+y2⋅b2=gcd(b1,b2),则 k1=y1⋅gcd(b1,b2)a2−a1,k2=−y2⋅gcd(b1,b2)a2−a1。
所以我们可以求出这个方程组的一个解为 x=a1+y1⋅gcd(b1,b2)a2−a1⋅b1。
很明显最小正整数解就是 xmin=xmodlcm(b1,b2),但由于 x 有可能是负数,所以取模时需要特殊处理。
接下来 EXCRT 的核心思想来了:
我们可以把 x=a1+y1⋅gcd(b1,b2)a2−a1⋅b1modlcm(b1,b2) 改写成同余方程,即:
x≡a1+y1⋅gcd(b1,b2)a2−a1⋅b1modlcm(b1,b2)(modlcm(b1,b2))
这时,我们就成功把两个同余方程合并为了一个!
所以只要一直合并下去,就能得到最终的解了。
模板题代码:(需要高精所以开了 __int128)
#include <iostream>
#include <cstdio>
using namespace std;
int n;
__int128 a[100005],b[100005];
inline __int128 read()
{
	__int128 s=0;
	int w=1,ch=getchar();
	while(ch<'0'||ch>'9') ch=='-'?w=-1,ch=getchar():ch=getchar();
	while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return s;
}
void writen(__int128 x)
{
	if(x<0) putchar('-'),writen(-x);
	x>9?writen(x/10),putchar(x%10|48):putchar(x|48);
}
inline __int128 lcm(__int128 a,__int128 b)
{
	__int128 tmpa=a,tmpb=b;
	__int128 t=a%b;
	while(t!=0)
	{
		a=b;
		b=t;
		t=a%b;
	}
	return tmpa*tmpb/b;
}
__int128 exgcd(__int128 a,__int128 b,__int128 &x,__int128 &y)
{
	if(a<b)
	{
		return exgcd(b,a,y,x);
	}
	if(b==0)
	{
		x=1;
		y=0;
		return a;
	}
	__int128 tmpx,tmpy;
	__int128 res=exgcd(b,a%b,tmpx,tmpy);
	x=tmpy;
	y=tmpx-a/b*tmpy;
	return res;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		b[i]=read();
		a[i]=read();
	}
	a[1]=(a[1]%b[1]+b[1])%b[1];
	for(int i=2;i<=n;i++)
	{
		__int128 rig=a[i]-a[i-1];
		__int128 y1,y2;
		__int128 g=exgcd(b[i-1],b[i],y1,y2);
		if(rig%g!=0)
		{
			puts("Impossible!");
			break;
		}
		y1*=rig/g;
		a[i]=a[i-1]+b[i-1]*y1;
		b[i]=lcm(b[i-1],b[i]);
		a[i]=(a[i]%b[i]+b[i])%b[i];
	}
	writen(a[n]);
	putchar('\n');
	return 0;
}