hihocoder 1297 数论四·扩展欧几里德(exgcd)

题目传送门:http://hihocoder.com/problemset/problem/1297

扩展欧几里德(exgcd)我一直学的不是太好,虽然中间的一些道理啊知道,但是每次碰到exgcd的题就都不会写,正负错啊,模错啊

今天小小总结下我自己的exgcd的经验把,不是讲原理,因为原理大家都看过很多,反正我记不住,套套模板就好了

首先一般都是构造ax+by=c,然后调用exgcd(a,b,d,x,y),求出d,x,y,如果c%d!=0时无解,如果题目让你求x,那么a=0时也是无解,下面以只求x为例

然后就是此时求出来的x是ax+by=d的解,要变成ax+by=c的解,还需要x=x*(c/d),此时是一个解

那么通解形式是啥样呢,x=x+(b/d)*t ,y=y-(a/d)*t;记住即可

所以如果你要求x的最小值,x是关于t的单调函数,根据(b/d)的正负性,取值即可

一般题目做到x都是大于等于0的,所以这里就说下x>=0时最小值怎么求

x>=0时,肯定是减去越多的abs(b/d)越好,所以x%abs(b/d),但是可能原来x用exgcd求解出来是负数,所以就加上abs(b/d),再模一下

所以答案ans=(x%abs(b/d)+abs(b/d))%abs(b/d);

这里以hiho一下的例题为例。

小Hi和小Ho周末在公园溜达。公园有一堆围成环形的石板,小Hi和小Ho分别站在不同的石板上。已知石板总共有m块,编号为 0..m-1,小Hi一开始站在s1号石板上,小Ho一开始站在s2号石板上。

小Hi:小Ho,你说我们俩如果从现在开始按照固定的间隔数同时同向移动,我们会不会在某个时间点站在同一块石板上呢?

小Ho:我觉得可能吧,你每次移动v1块,我移动v2块,我们看能不能遇上好了。

小Hi:好啊,那我们试试呗。

一个小时过去了,然而小Hi和小Ho还是没有一次站在同一块石板上。

小Ho:不行了,这样走下去不知道什么时候才汇合。小Hi,你有什么办法算算具体要多久才能汇合么?

小Hi:让我想想啊。。

提示:扩展欧几里德

输入

第1行:每行5个整数s1,s2,v1,v2,m,0≤v1,v2≤m≤1,000,000,000。0≤s1,s2<m

中间过程可能很大,最好使用64位整型

输出

第1行:每行1个整数,表示解,若该组数据无解则输出-1

样例输入
0 1 1 2 6
样例输出
5

假设时间t,小hi走到了(s1+v1*t)%m的位置,小ho走到了(s2+v2*t)%m的位置,然后这两个位置相同

(s1+v1*t)%m=(s2+v2*t)%m =>  s1+v1*t +m*a = s2+v2*t => (v1-v2)*t+m*a=s2-s1

这不就是我们exgcd的形式么,然后带入exgcd(v1-v2,m,d,x,y),求出d,x,y

如果(s2-s1)%d!=0||v1==v2 ,输出-1

否则就是构造最小解,x=x*(s2-s1)/d, x=(x%abs(m/d)+abs(m/d))%abs(m/d);

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX           5000005
#define   MAXN          100005
#define   maxnode       10
#define   sigma_size    2
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
const double inf   = 1e18;
const double eps   = 1e-9;
const LL     mod   = 1e9+7;
const ull    mxx   = 1333331;

/*****************************************************/
inline void RI(int &x) {
      char c;
      while((c=getchar())<'0' || c>'9');
      x=c-'0';
      while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/

void exgcd(LL a,LL b,LL &d,LL &x,LL &y){
    if(!b){x=1;y=0;d=a;return;}
    else {exgcd(b,a%b,d,y,x);y-=a/b*x;}
}
LL labs(LL a){
    if(a<0) return -a;
    return a;
}
int main(){
    //freopen("in.txt","r",stdin);
    LL s1,s2,v1,v2,m;
    cin>>s1>>s2>>v1>>v2>>m;
    LL x,y,d;
    exgcd(v1-v2,m,d,x,y);
    if((s2-s1)%d!=0||v1==v2) printf("-1\n");
    else{
        x=x*(s2-s1)/d;
        LL mo=labs(m/d);
        cout<<(x%mo+mo)%mo<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值