洛谷P1516 青蛙的约会

简明题意

简明题意
更好的题面阅读体验,请见 LibreOJ #10209. 「一本通 6.4 例 1」青蛙的约会

PS: 部分前置知识请移步 之前在 CSDN 写的相关博客 。窃以为题解应当模块化,读者自取想学的知识, 因此其中关于同余方程的部分就不细解释了。

题目分析

不太愉悦地推式子

地球是两极稍扁赤道略鼓的椭球体。 纬线圈可以近似看成圆。

如果不同意上面的说法,可以关闭此篇题解了(滑稽

看完题面,发现是道追及问题,而且是在一个圆上。

先从直线运动的模型来分析,设时间为 $ t (t \in \mathbb{N}^*) $ 那么青蛙 $ A $ 的位移 $ x_a = x + mt$,青蛙 $ B $ 的位移为 $ x_b = y + nt$。当他们相遇时,满足 $ x_a = x_b $ 即 $ x + mt = y + nt $ 。

再转到圆上,我们知道其周长为 $ L $, 若两青蛙相遇,则满足

x a ≡ x b m o d    L x_a \equiv x_b \mod L xaxbmodL

即:

x + m t ≡ y + n t m o d    L x + mt \equiv y + nt \mod L x+mty+ntmodL

移项后有:

t ( m − n ) ≡ y − x m o d    L t(m - n) \equiv y - x \mod L t(mn)yxmodL

氦,最后还是得转化成: $ t(m - n) - (y - x) = kL $ 有整数解

PS: $ t(m - n) $ 和 $ y - x $ 对 $ L $ 取余相等就相当于 $ t(m - n) $ 和 $ (y - x) $ 刚好相差整数个 $ L $。

不希望有负号并且希望式子更加简单,我们再变换一下,也就是 $ kL + t(n - m) = x - y $ 有整数解

划出来这里的未知量: $ t $ 和 $ k $, 不定方程有了,剩下的事就是数学老师的事了

这时敏锐的你发现这是 A x + B y = C Ax + By = C Ax+By=C 的形式,根据裴蜀定理,当且仅当 $ \gcd(A, B) \mid C $ 有整数解。如果有解,我们可以用扩展欧几里得算法求出 $ A $ 和 $ B $ 的其中一个解。

愉悦地码代码

我们的目标是解这个方程:

k l + t ( n − m ) = x − y kl + t(n - m) = x - y kl+t(nm)=xy

第一步: 输入,把数据整理成上述格式。

long long x, y, m, n, l;
scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &l);
long long X = l, Y = n - m, C = x - y;  // 自行对比 AX + BY = C

第二步: 处理系数。

if (Y < 0) Y = -Y, C = -C;   // 系数必须为正整数
LL g = __gcd(X, Y);          // 判断是否有解,化简方程
if (C % g) { printf("Impossible"); return 0; } // 互质,无解

我们不能只背定理不记条件(hhh,反正不用证), 根据题意 $ L $ 一定为正, 而 $ n - m $ 不一定,如果它小于 $ 0 $,则需要把整个方程两边乘 $ -1 $,这里没写 X = -X是因为解出来只不过是系数正负的区别,不影响。

如果 $ \gcd(A, B) \nmid C $,则方程无符合题意的整数解,输出 Impossible

第三步: 解不定方程。

X /= g; Y /= g; C /= g; // 化简
pLL sol = exgcd(X, Y);  // 求解
sol.second *= C;        // 通过 ... = gcd(x, y) 的解求原方程的解,此时 x, y 已经互质

扩展欧几里得求解的是 $ Ax + By = \gcd(A, B) $ 的解, 最后需要将结果同乘 $ \frac{C}{\gcd(A, B)}$ 才能得到我们需要的答案。这里我们通过同除其最大公因数使其互质,则只需要将结果乘 $ C $ 就行了。

第四步: 处理并输出答案。

printf("%lld", (sol.second % X + X) % X); // 最小正整数解

由于题目让求最小正整数解,而扩展欧几里得过程的结果并不能保证是,因此我们对答案再进行取模(要 $ +X $ 是为了避免负数)。

代码

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> pLL;
pLL exgcd(LL a, LL b)
{
	if(b == 0) return make_pair(1, 0);
	pLL t = exgcd(b, a % b);
	pLL res;
	res.first = t.second;
	res.second = t.first - (a / b) * t.second;
	return res;
}
int main() 
{
    LL x, y, m, n, l;
    scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &l);
    LL X = l, Y = n - m, C = x - y; // 经过化简(或由同余方程推导)kl + t(n - m) = x - y
    if (Y < 0) Y = -Y, C = -C;      // 系数必须为正整数
    LL g = __gcd(X, Y);             // 判断是否有解,化简方程
    if (C % g) { printf("Impossible"); return 0; } // 互质,无解
    X /= g; Y /= g; C /= g;         // 化简
    pLL sol = exgcd(X, Y);          // 求解
    sol.second *= C;                // 通过 ... = gcd(x, y) 的解求原方程的解,此时 x, y 已经互质
    printf("%lld", (sol.second % X + X) % X); // 最小正整数解
    return 0;
}

跑的还挺快的,自带 O2 优化的 LibreOJ 只跑了 10ms

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值