我作为一个初中蒟蒻,听y大视频听了5遍还不懂,快哭了。然后终于(好像)搞懂,写成题解加深一下记忆…
1. 将式子等价转换
对于每两个式子(我们考虑将其合并):
$x \equiv m_1 (\%\ a_1)$
$x \equiv m_2 (\%\ a_2)$
则有:
$x = k_1 * a_1 + m_1$
$x = k_2 * a_2 + m_2$
进一步:
$k_1 * a_1 + m_1 = k_2 * a_2 + m_2$
移项:
$k_1 * a_1 - k_2 * a_2 = m_2 - m_1$
也就是:
① $k_1 * a_1 + k_2 * (-a_2) = m_2 - m_1$
也就是我们需要找到一个最小的$k_1, k_2$,使得等式成立(因为要求$x$最小,而$a$和$m$都是正数)。
2. 用扩展欧几里得算法找出一组解
我们已知$a_1,m_1,a_2,m_2$,可以用扩展欧几里得算法算出一个$k’_1, k’_2$使得:
$k’_1 * a_1 + k’_2 * (-a_2) = gcd(a_1, -a_2)$
无解判断:
若$gcd(a_1, -a_2) \nmid m_2 - m_1$,则无解。
我们设$d = gcd(a_1, -a_2),y = \frac{(m_2 - m_1)}{d}$
承接上文,我们只需让$k_1, k_2$分别扩大$y$倍,则可以找到一个$k_1, k_2$满足①式:
$k_1 = k’_1 * y, k_2 = k’_2 * y$
3. 找到最小正整数解
我们知道一个性质:
②$k_1 = k_1 + k *\frac{a_2}{d}$
$k_2 = k_2 + k *\frac{a_1}{d}$
$k$为任意整数,这时新的$k_1, k_2$仍满足①式。
证明:
将新的$k_1, k_2$带入式子得:
$(k_1+k\*\frac{a_2}{d})*a_1+(k_2+k\*\frac{a_1}{d})\*(-a_2)=m_2-m_1$
拆出来:
$k_1\*a_1+k\*\frac{a_2\*a_1}{d}+k_2\*(-a_2)+k\*\frac{a_1\*(-a_2)}{d}=m_2-m_1$
交换一下顺序,把负号拆出来:
$k_1\*a_1+k_2\*(-a_2)+k\*\frac{a_2 \* a_1}{d}-k\*\frac{a_1 \* a_2}{d}=m_2-m_1$
那个同加同减可以消掉:
$k_1\*a_1+k_2\*(-a_2)=m_2-m_1$
这个式子和①是一样的,因①成立,故此式也成立。
要找一个最小的非负整数解,我们只需要让
$k_1 = k_1 \%\ abs(\frac{a_2}{d})$
$k_2 = k_2 \%\ abs(\frac{a_1}{d})$
即可找到当前最小的$k_1, k_2$的解,即此时的$k$为$0$。
$Q$:此处为什么要取绝对值呢
$A$:因为不知道$\frac{a_2}{d}$的正负性,我们在原基础上要尽量减多个$abs(\frac{a_2}{d})$,使其为正整数且最小。
4. 等效替代:
由②式带入
新的$x$为:
$x = (k_1 + k * \frac{a_2}{d}) * a_1 + m_1$
$= k_1 * a_1 + m_1 + k * \frac{a_2 * a_1}{d}$
$= k_1 * a_1 + m_1 + k * lcm(a_1, a_2)$ ③
$Q$:这里,$k$都为$0$了,为什么还要算呢?
$A$:因为这只是前两个式子得最小$k$,有可能遇到下一个式子后面被迫要扩大
在③中,我们设$a_0 = lcm(a_1, a_2), m_0 = k_1 * a_1 + m_1$
那么:
③ $ = k * a_0 + m_0$
这个形式与一开始我们分解的形式是不是特别像呢?
没错!假设之后又来了一个$a_3, m_3$
我们只需要继续找:
$x = k * a_0 + m_0 = k_3 * (-a_3) + m_3$,那么问题又回到了第一步。
5. 总结
我们的做法相当于每次考虑合并两个式子,将这$n$个式子合并$n - 1$次后变为一个式子。最后剩下的式子就满足我们的答案。
注意:
-
$lcm(a_1, a_2)$和$\% \frac{a_2}{d}$,需要取绝对值。又因为$d = gcd(a_1, -a_2)$,我们不知道$a_1$的正负性(可能是上一步推过来的)。
-
$\% \frac{a_2}{d}$,需要取绝对值, 膜负数的话,不会取到正解;
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long LL;
int n;
LL exgcd(LL a, LL b, LL &x, LL &y){
if(b == 0){
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
LL inline mod(LL a, LL b){
return ((a % b) + b) % b;
}
int main(){
scanf("%d", &n);
LL a1, m1;
scanf("%lld%lld", &a1, &m1);
for(int i = 1; i < n; i++){
LL a2, m2, k1, k2;
scanf("%lld%lld", &a2, &m2);
LL d = exgcd(a1, -a2, k1, k2);
if((m2 - m1) % d){ puts("-1"); return 0; }
k1 = mod(k1 * (m2 - m1) / d, abs(a2 / d));
m1 = k1 * a1 + m1;
a1 = abs(a1 / d * a2);
}
printf("%lld\n", m1);
return 0;
}
初中生大佬牛逼!感觉我这几年白活了一样捏qwq
我上大学之前就没接触过电脑,估计我也白活了
我也是呜呜呜
我也是呜呜呜
我上大学后才开始接触算法
补充一下使用 线性丢番图方程 求$k1$所有的解
$k_1\cdot a_1 + k_2\cdot (-a_2) = m_2 - m_1$
$k_1\cdot a_1 + k_2\cdot (-a_2) = gcd(a_1, -a_2)$
对a1, a2使用扩展欧几里德函数得到d
如果$d|m_2-m_1$则有解。两边乘$\frac{m_2-m_1}{d}$ 右边就变为了$m_2-m_1$
有解的情况下得到一组特解
$k_1 = k_1\cdot \frac{m_2-m_1}{d}$
$k_2 = k_2\cdot \frac{m_2-m_1}{d}$
注意这里$k_1$表示特解
### 找到所有的解
将一组特解带回方程
$k_1 += a_2/d$的同时$k_2 -= a_1/d$,方程右边不变
于是得到所有情况的解集
$k1 = k_1 + (\frac{a_2}{d})\cdot k$
$k2 = k_2 + (\frac{a_1}{d})\cdot k$
#### 找到最小正整数解
模尽倍数后,剩下的余数就是最小的正整数解
$k1 = k_1 \bmod abs(\frac{a_2}{d})$
$k2 = k_2 \bmod abs(\frac{a_1}{d})$
代码中表现为
新的$x$为:
$$ x= (k_1 + \frac{a_2}{d} \cdot k) \cdot a_1 + m_1 \\ = k_1\cdot a_1 + m_1 + \frac{a_2\cdot a_1}{d} \cdot k $$
$k_1\cdot a_1 + m_1$ 为合并后的$a_1$,$\frac{a_2\cdot a_1}{d} \cdot k$为新的$m_1$
你最后的m1和a1写反了
是的感谢纠正!
写得真好,可以放到你的题解里面
妙!!大佬写个题解吧
所有过程都明白但是为什么最后输出的是m1的值而不是x的值呢?
这里引用楼主的回答,可以看出 $m1$ 和 $x$ 是等价的。实际代码中只在求解$m1$,所以这里输出的是 $m1$
# 初等数论之求解二元不定方程
定理1:
设二元一次不定方程
$$ ax + by = c \\hspace{1cm} (1) $$
(其中a, b, c是整数, 且a, b都不是0), 假定有一组整数解,$x=x_0, y=y_0$。
则原式的一切整数的通解可以表示成
$$ \\left\\{ \\begin{aligned} x = x_0 - \\frac b {(a, b)}t, & \\\\ & (t \\in z) \\\\ y = y_0 + \\frac a {(a, b)}t, & \\\\ \\end{aligned} \\right. $$
定理2:
$\\hspace{1cm}(1)$式有整数解等价于$(a, b)|c$。
这是第三步的解答
扩展欧几里得算法就是求解二元不定方程的手段之一
x的通解和y的通解表示,正负号是不是写反了
hh,确实,感谢指正 😊
问题是我咋改哇,我得重新删掉了0.0
通解正负号写反了,细节可以看我这个题解
😊
哪个正哪个负都一样吧,=c的特解加上=0的通解
。。。写反了锤子,加正数和减负数不是一样的吗?
没有写错啊
看了一圈,就你这个有用(虽然正负号搞反了)。这个结论非常关键,但是好像所有人都认为它理所当然?谢谢你,数论侠。
多谢你的解释,我想了很久没想明白这个问题
这就是大佬 我大三了还要看大佬初中写的题解呜呜呜
我大一,一个月后大二,也在看初中生写的题解
通解那里有点问题吧,应该是证明怎么得到通解的,而不是用带回去证明等式成立。(虚心交流,没有找茬哈)
为什么直接k=0了?k不是有可能等于负数吗?
orz,Orz,or2,Or2。等效替换那卡我半天,一直想不通k为0的这一点,原来是增加新的式子后会被迫扩大。上古真神墨染空,太强啦,%%%
为什么要说自己是初中的?
这就是大佬 我大三了还要看大佬初中写的题解呜呜呜
请问有没有一种可能
k1可以是负数
但是k1*a1+m1>0
这样的话是不是就可以求得比k1 = mod(k1 * (m2 - m1) / d, abs(a2 / d))更小的解了
好奇这样想对不对麻烦大佬帮我看看
写的太好了!
其实这道题不需要abs
题目说的很清楚了a的范围
当时我太菜了呜呜
哈哈没事 我也菜
确实,我也发现不加abs()可以ac,是不是a1,a2一直是正数,所以没必要加绝对值?
初中大佬太强了,自愧不如
大二数学系学生泪目,啥也不会
别急,俺也一样
麻了,白活了
我也是
大佬nb!来自五线城市的初三蒟蒻对你五体投地!
既然 k 为任意整数,那通解那里为啥还要除以一个gcd(a1, -a2)呢
搞一个最小的
为什么一定要k*(a1/d)?这个d的必要性在哪?
讲的太清楚了,学了两年半再看这篇文章恍然大悟
$%$学了多久?
总之就是不推荐用中国剩余定理做
是不能
因为不保证两两互质,是能使用扩展CRT了吧