【二元不定方程】POJ 青蛙的约会

传送门

首先,根据题意,我们可以列出同余方程:

( x + k m ) % L = = ( y + k n ) % L (x+km) \% L= = (y+kn)\%L (x+km)%L==(y+kn)%L

( x + k m ) − ( y + k n ) = a L (x+km)-(y+kn)=aL (x+km)(y+kn)=aL

( m − n ) k + y − x = a L (m-n)k+y-x=aL (mn)k+yx=aL
这就是一个二元一次不定方程了
可以直接套用exgcd

剩下的有一点点麻烦
题目要求最小的正整数k 所以我们还要找符合条件的这个特解
首先用exgcd搞一个特解出来
利用exgcd解 a x + b y = c , d = ( a , b ) ax+by=c,d=(a,b) ax+by=c,d=(a,b)的结论,我们知道x的通解是 x + b d x+\frac{b}{d} x+db
在这个解为正整数的条件下 对于每一个解,我们可以快速求出最小正整数解为 ( x + b d ) % b d (x+\frac{b}{d})\%\frac{b}{d} (x+db)%db
[可以想成通解是一个等差数列,是最小的那个数加上很多个 b d \frac{b}{d} db 构成的 所以每个解求余 b d \frac{b}{d} db就是原来那个最小解]

所以在用exgcd求出解后 如果特解是负数 我们一直加 b d \frac{b}{d} db知道它为正 就是最小正整数解

如果求出的解本身为正 我们就求余 b d \frac{b}{d} db,就是答案

#include
#include
#include
using namespace std;
#define MAXN 1005
#define LL long long
LL gcd(LL a,LL b)
{
    return b?gcd(b,a%b):a;
}
void exgcd(LL a,LL b,LL &d,LL &x,LL &y)
{
    LL x0=x,y0=y;
    if(b==0)
    {
        d=a,x=1,y=0;
        return ;
    }
    else
    {
        exgcd(b,a%b,d,x0,y0);
        x=y0;
        y=x0-a/b*y0;
    }
}
int main()
{
    LL x,y,m,n,l;
    scanf("%lld %lld %lld %lld %lld",&x,&y,&m,&n,&l);
    LL a=m-n,b=l,c=y-x;
    if(a<0) a+=l;//把a变成正数 由于是环形 相对位置不改变 不影响结果 
    LL d=gcd(a,b);
    if(c%d!=0)
    {
        printf("Impossible\n");
        return 0;
    }
    exgcd(a,b,d,x,y);
    x=x*(c/d);
    while(x<0)
        x+=(b/d);
    x=x%(b/d);
    printf("%lld\n",x);
    return 0;
}

另外就是注释的地方 相当于确保a为整数
不知为什么加了这个东西就要快很多 当然加不加在poj上都还是可以过


此题在HihoCoder上也有一个比较相似的题目,可以了解一下 感觉这个网站上的提示还比较到位 有循循善诱的感觉
传送门

你可能感兴趣的:(数学)