题解 P4777 【模板】扩展中国剩余定理(EXCRT)
上课根据回忆重新推了一遍,发现不会同余方程了。
题意
给定 $n$ 组非负整数 $a_i, b_i$ ,求解关于 $x$ 的方程组的最小非负整数解。
$$ \begin{cases} x \equiv b_1\ ({\rm mod}\ a_1) \\ x\equiv b_2\ ({\rm mod}\ a_2) \\ … \\ x \equiv b_n\ ({\rm mod}\ a_n)\end{cases} $$
推导
考虑依次合并两个方程,直到剩下一个为止。
$$ \begin{cases} x \equiv b_1\ ({\rm mod}\ a_1) \\ x\equiv b_2\ ({\rm mod}\ a_2) \\ \end{cases} $$
首先将方程转化为:
$$ \begin{cases} x = b_1+a_1k_1 \\ x=b_2+a_2k_2 \\ \end{cases} $$
连立为:
$$ a_1k_1-a_2k_2=b_2-b_1 $$
令 $d=\gcd(k_1,k_2),p_1=\frac{a_1}{d},p_2=\frac{a_2}{d}$。
所以原式为:
$$ p_1k_1-p_2k_2=\frac{b_2-b_1}{d} $$
通过 exgcd,得到:
$$ p_1\lambda_1+p_2\lambda_2=1 $$
所以 $k_1$ 的通解为 $\frac{b_2-b_1}{d}\lambda_1$。
原方程组的特解为:$b_1+a_1k_1=b_1+a_1\lambda_1\frac{b_2-b_1}{d}$。
根据定理。(引用同学的,我懒得写了)
原方程的合并结果为:
$$ x \equiv a_1\lambda_1\frac{b_2-b_1}{d}\ ({\rm mod}\ \text{lcm}(b_1,b_2)\ ) $$
这么一直做下去就行了。
代码
#include<algorithm>
#include<iostream>
#define int __int128
using namespace std;
template <typename __Tp> void read(__Tp &x) {
int f = x = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = 1;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
if (f) x = -x;
}
void read(char &ch) { for (ch = getchar(); isspace(ch); ch = getchar()); }
template <typename __Tp1, typename ...__Tp2> void read(__Tp1 &x, __Tp2 &... y) { read(x), read(y...); }
template <typename __Tp> void write(__Tp x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar(x % 10 + 48);
}
void write(char ch) { putchar(ch); }
void write(const char *s) { for (; *s; ++s) putchar(*s); }
template <typename __Tp1, typename ...__Tp2> void write(__Tp1 x, __Tp2 ... y) { write(x), write(y...); }
struct equation
{
int a,m;
}a[100005],s;
int n,p;
void exgcd(int a,int b,int &x,int &y)
{
if(!b){x=1;y=0;return;}
exgcd(b,a%b,x,y);
int z=x;x=y;y=z-y*(a/b);
}
equation work(equation x,equation y)
{
equation ans;
int gcd=__gcd(x.m,y.m),da=y.a-x.a;
int X,Y;
exgcd(x.m,y.m,X,Y);
ans.m=x.m/gcd*y.m;
X=(X*(da/gcd))%ans.m;
ans.a=((X*x.m)%ans.m+x.a+ans.m)%ans.m;
return ans;
}
signed main()
{
read(n);
for(int i=1;i<=n;i++)read(a[i].m,a[i].a);
s=a[1];
for(int i=2;i<=n;i++)s=work(a[i],s);
write(s.a%s.m);
}