题目描述
大圣在佛祖的手掌中。
我们假设佛祖的手掌是一个圆圈,圆圈的长为 n,逆时针记为:0,1,2,…,n−1,而大圣每次飞的距离为 d。
现在大圣所在的位置记为 x,而大圣想去的地方在 y。
要你告诉大圣至少要飞多少次才能到达目的地。
注意:孙悟空的筋斗云只沿着逆时针方向翻。
输入格式
有多组测试数据。
第一行是一个正整数 T,表示测试数据的组数;
每组测试数据包括一行,四个非负整数,分别为如来手掌圆圈的长度 n,筋斗所能飞的距离 d,大圣的初始位置 x 和大圣想去的地方 y。
样例
输入:
2
3 2 0 2
3 2 0 1
输出:
1
2
首先,先说一下扩展欧几里得定理:指对于任意正整数对(a,b),一定存在非零整数x和y,使得 ax+by=gcd(a,b);
我们来分情况讨论一下扩展欧几里得定理:
(1)当b = 0时,a和b的最大公约数为a.则x = 1;
(2)当b ≠ 0时,by+(a mod b)x = gcd(a,b)
->by+(a - a/b * b)x = gcd(a,b)
->ax+b(y - a/b * x) = gcd(a,b)
即当我们用扩展欧几里得定理求x和y时,欧几里得定理每递归一次x不用变,y->y-a/b * x即可
如果我们求出x和y的一对,我们记为x0和y0
那么其他的x和y可以通过x0,y0表示:
令a’=a/gcd(a,b) , b’=b/gcd(a,b);
那么其他的x和y可以表示为:x=x0+kb’,y=y0-ka’;
我们来证明一下:
ax0+by0 = gcd(a,b)
ax+by = gcd(a,b)
如果x0比x小,那么y0就应该比y大,这样加起来的结果才可能相同
所以,a(x-x0) = b(y0-y);
两边同时除以一个gcd(a,b)
得到:a’(x-x0) = b’(y0-y)
那么b’就应该能整除a’(x-x0),又因为a’和b’互质,所以b’应该能整除(x-x0)
所以(x-x0) = kb’
则 x = x0+kb’
y = y0-ka’
这里的k可正可负
回到这道题,这题的题意是让我们求x+bd = y+an ,整理得:-an+bd = y-x
但是我们得扩展欧几里得定理只能求 -an+bd = gcd(n,d)中的a和b,但是很容易看出y-x应该为gcd(n,d)的整数倍
否则无解。
因此我们只需要判断y-x是否为gcd(n,d)的整数倍就能判断是否有解了。
若有解我们利用扩展欧几里得定理就可以求得-an+bd = gcd(n,d)中的a和b,然后将a和b扩大 (y-x)/gcd(n,d)倍
就可以得到一组a0和b0,因为之前我们证过了获得一组解后其他解就可以表示出来了,我们只需要求一下所有解中的最小值就可以了(注意我们的b只能是正数,你总不能让大圣反着翻吧)
即:
求一下b = b0+k*(n/gcd(n,d))的最小值即可
minb = b0%(n/gcd(n,d));
C++ 代码
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long LL;
LL exgcd(LL a,LL b,LL &x,LL &y){
if(!b){
x = 1,y = 0;
return a;
}
int d = exgcd(b,a%b,y,x);
y -= a/b*x;
return d;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
LL n,d,x,y,a,b;
scanf("%lld%lld%lld%lld",&n,&d,&x,&y);
int gcd = exgcd(n,d,a,b);
if((y-x)%gcd){
printf("Impossible\n");
}else{
b*=(y-x)/gcd;
n/=gcd;
printf("%lld\n",(b%n+n)%n);
}
}
return 0;
}
想问一下,最后那里,不是应该先用b=b0+k(n/gcd(n,d))求出最小的b,再用(y-x)/gcd去乘以他,才是吗。因为b=b0+k(n/gcd(n,d))这个式子不是用在an+bd=gcd(n,d),为什么可以先乘(y-x)/gcd再去求最小啊,先乘了不就不是an+bd=gcd(n,d)这个式子,还能用b=b0+k*(n/gcd(n,d))求最小值吗
同问
y-x有可能是负数,先算出最小的再拿(y-x)/gcd乘以它就出问题了,但是先乘到b里面就没关系,因为就算乘完以后b是负数,b取完余以后肯定还是正数,而且是最小的正数
b乘完为什么a不变
我也看不懂他这块,所以我自己测试了一下
if((y-x)%gcd){
printf(“Impossible\n”);
}else{
n/=gcd;
b%=n;
b=(y-x)/gcd;
printf(“%lld\n”,(b%n+n)%n);
}
不需要先×(y-x)/gcd也能过
b变了,a肯定也变了呀,但是b = b0+k*n/gcd;这里表示任意的b时,没用到a呀,a是变量,n,d是常量,别跟之前证明扩展欧几里得的过程搞混了。
为什么最小值是b0%(n/gcd(n,d)??
看公式b = b0+k(n/gcd(n,d)) ,b0的作用是用b0来表示所有的b,所以这里k的作用就是来帮助实现b0的作用,所以k可以取任意整数,b0+k(n/gcd(n,d)) 要取到最小值,就需要k最小,k最小取到—b0/(n/gcd(n,d))。
懂了谢谢你!
问一下大佬,你说k取最小,直接取0不就是最小的了,按照你这样说把k=b0/(n/gcd(n,d))带入原式得到b=2b0
k可以取负值的呀,mink = —b0 /(n/gcd(n,d)),这样b就是最小了
谢谢大佬,我悟了
没有懂k取到最小值跟b0%(n/gcd(n,d)有什么关系呢?
首先把k当作一个变量,b0和n/gcd(n,d)都是常数,那k要取多少才能使b(b>0)取到最小值呢,
是不是 b0被扩大了之后变成了b,然后b = bmin + k*n/gcd(n,d),b / (n/gcd(n,d)) = k余bmin,因此b % (n/gcd(n,d)) = bmin,整个过程是这样的吗?
是的
感谢感谢
厉害厉害
问一下 最后b扩大呢么多倍数 为什么还要除gcd?
为什么要用long long 欸,我觉得int也可也欸
你试试不就知道了
大佬请问为什么最后b是+上k*(n/gcd)呢,为什么是以n/gcd的步频来变化的鸭
想问个问题是,为什么y - x 不是gcd(n,d) 的整数倍就无解呢? 这是不是只能说用exgcd方法是无解的,但是还是可能有其他解呀?
这个是裴蜀定理的内容,一定存在整数x,y满足ax+by = gcd(a,b),且ax+by一定是gcd(a,b)的整数倍
噢,似乎来看对于任意的ax+by都是被gcd(a,b)整除的
(2)当b ≠ 0时,by+(a mod b)x = gcd(a,b)这句话一直不理解,难道不应该是bx + (a mod b)y 吗,我看其他地方关于扩展欧几里得定理时都是写的这个,那里退出来的是x = y, y = (x - (a/b)*y), 结果也是一样可行的的,我一下子就理解了那种,但y总这个式子我看了半天也不知道怎么来的
有没有一种可能是y总手误了
“得到:a(x-x0) = b(y0-y)
那么b就应该能整除a(x-x0),又因为a和b互质,所以b`应该能整除(x-x0)”
为啥啊b就能整除a(x-x0)了,这边还有(y0-y)没处理呢啊??
整除的就是(y0-y)呀,这个东西给整除了
同问,这里不理解
因为 (y0 - y) 是整数啊,a(x - x0) / b 不是等于 (y0 - y)吗 ,你是不是弄错了整除的意思
b 是被除数
大佬请问最小值为什么就是这个呢 b%n+n)%n
负数
你总不能让大圣反着翻吧)
讲的太好了!顶
请问 b = b0+k*(n/gcd(n,d)) 这个式子是怎么来的Qaq
前面推的公式,b=b0-ka
https://wenku.baidu.com/view/967795fbaef8941ea76e0576.html
这是个定理,如果不懂,看这个链接的定理2,有介绍