EXCRT 其实和 CRT 半毛钱关系都没有……
我们在 CRT 中需要用到求逆元的操作:
但是如果 不满足两两互质,我们就无法求出逆元,这样 CRT 就用不了了。
为了解决这个问题,求出 不一定两两互质时同余方程组
的解,EXCRT 就诞生了。
首先考虑两个方程的情况:
可以转化为这样:
即:
移项:
这个柿子很明显能使用 exgcd 求解。
很明显,若 那么无解。
设 满足 ,则 。
所以我们可以求出这个方程组的一个解为 。
很明显最小正整数解就是 ,但由于 有可能是负数,所以取模时需要特殊处理。
接下来 EXCRT 的核心思想来了:
我们可以把 改写成同余方程,即:
这时,我们就成功把两个同余方程合并为了一个!
所以只要一直合并下去,就能得到最终的解了。
模板题代码:(需要高精所以开了 __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;
}