P1516 青蛙的约会 解题报告
P1516 青蛙的约会 题解
\(\quad\)真是一道酣畅淋漓的数论题呐
1. 原题 P1516 青蛙的约会
2. 简化题意
\(\quad\)在一个长度为 \(l\) 圆环上,青蛙A在 \(x\) 点,青蛙B在 \(y\) 点,青蛙A一步能走 \(m\) 米,青蛙B一步能走 \(n\) 米,问:两只青蛙何时能站在同一点上
3. 转化题意
\(\quad\)我们设跳 \(k\) 次后两只青蛙会相遇,根据题意,我们可以得到一个同余方程:
根据同余方程的性质,我们可以将这个柿子转化一下:
将左边提取公因式可以再次转化:
这个柿子是什么意思呢?
不难发现,\(k \cdot (m - n)\) 与 \(y - x\) 的差是 \(l\) 的整数倍,那么我们可以再代入一个未知数 \(z \in \mathbb{Z}\) (\(z\)属于整数集)
那么我们将柿子进一步转化,转化为等式:
我们将所有未知数转移到左边可以得到:
仔细观察一下这个柿子,是不是有点眼熟?\(a \cdot x + b \cdot y = c\) 是不是恍然大悟?我去,这不就是一个 不定方程 吗!想到这里,你应该可以写了,只需要一个 拓展欧几里得 即可求解
4. 如何用 拓展欧几里得 ?
为了便于下面的理解,我们将用 \(a\) 代替 \(m - n\),用 \(b\) 代替 \(l\),用 \(c\) 代替 \(y - x\),\(z\) 和 \(k\) 代表一个未知数
那么我们可以列出一个便于理解的柿子
什么情况下无解呢?
我们知道exgcd是用来求解形如 \(a \cdot x + b \cdot y = gcd(a, b)\) 的不定方程,根据这个我们很容易得到 当且仅当 \(c\) 为 \(gcd(a, b)\) 的倍数时,\(a \cdot k + b \cdot (-z) = c\) 有解,所以就可以判断 if((b - a) % __gcd(n - m, l) != 0) 时,输出 Impossible
具体如何求解?
根据exgcd,我们求解了方程 \(a \cdot k + b \cdot (-z) = gcd(a, b)\) 得到了一组解 假设其为 \((k', z')\),我们需要根据这个方程的这组解,求解出属于这道题的特解 \(k_特\)
观察这两个方程:
我们考虑将第一个方程转化为第二个方程,只需要左右两边同时乘 \(\frac{c}{gcd(a, b)}\):
所以:
很好,恭喜你解决了这道题:
5. 参考代码
不建议直接看,建议自己把柿子推一遍,自己写
#include<bits/stdc++.h>
#define int long long
using namespace std;
int exgcd(int a, int b, int &x, int &y){ // 精简版exgcd
if(!b) return x = 1, y = 0, a;
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
signed main(){
int a, b, n, m, l;
cin>>a>>b>>n>>m>>l;
int x, y;
int d = exgcd(n - m, l, x, y); // n-m只是取了m-n的相反数,不取相反数会出问题
if((b - a) % d != 0) return 0 * puts("Impossible");
int t = abs(l / d);
x *= (b - a) / d;
cout<<(x % t + t) % t;
return 0;
}

浙公网安备 33010602011771号