五指山||C 循环(扩展欧几里得)

扩展欧几里得

简单证明
在这里插入图片描述

1299. 五指山

五指山
在这里插入图片描述
思路
1.扩展欧几里得 acwing 877
在求最大公约数同时求得一组 x,y满足gcd(a, b) = a * x + b * y
gcd ⁡ ( a ,   b ) = g c d ( b ,   a   %   b ) \gcd(a,\ b) = gcd(b,\ a\ \%\ b) gcd(a, b)=gcd(b, a % b)
a x + b y = b ∗ x 0 + ( a % b )   ∗ y 0 ax+by = b*x0 + (a \% b)\ *y0 ax+by=bx0+(a%b) y0
由于 a % b = a − ⌊ a b ⌋   ∗ b a\%b = a - \lfloor\frac{a}{b}\rfloor\ *b a%b=aba b
整理得 a x + b y = a ∗ y 0 + b   ∗ ( x 0 − ⌊ a b ⌋   ∗ y 0 ) ax+by = a*y0 + b\ *(x0-\lfloor\frac{a}{b}\rfloor\ *y0) ax+by=ay0+b (x0ba y0)
即递归根据下一层得x,y求得当前层得x,y
x = y 0 x=y0 x=y0
y = x 0 − ⌊ a b ⌋   ∗ y 0 y=x0-\lfloor\frac{a}{b}\rfloor\ *y0 y=x0ba y0
2.
a   ∗ x 0 + b   ∗ y 0 = g c d ( a , b ) = d a\ *x0+b\ *y0=gcd(a,b)=d a x0+b y0=gcd(a,b)=d
扩展欧几里得求得x0,y0
x,y满足 x = x 0 ± k   ∗ b d x=x0±k\ *\frac{b}{d} x=x0±k db y = y 0 ∓ k   ∗ a d y=y0∓k\ *\frac{a}{d} y=y0k da
3.
本题可抽象出 x + a d ≡ y ( m o d   n ) x+ad≡y(mod\ n) x+ady(mod n)
x + a d = y + b n x+ad=y+bn x+ad=y+bn 此处的a和b为变量
整理得 a d − b n = y − x ad-bn=y-x adbn=yx
由扩展欧几里得 a d − b n = g c d ( d ,   n ) ad-bn=gcd(d,\ n) adbn=gcd(d, n) 要使得a有解需要满足 y − x y-x yx g c d ( d , n ) gcd(d, n) gcd(d,n)的倍数
题目求a的最小值,由
2.
a = a 0 + k   ∗ n g c d ( d ,   n ) a=a0+k\ *\frac{n}{gcd(d,\ n)} a=a0+k gcd(d, n)n
a 0 = a   m o d   n g c d ( d ,   n ) a0=a\ mod\ \frac{n}{gcd(d,\ n)} a0=a mod gcd(d, n)n 为所求值

样例输入:

2
3 2 0 2
3 2 0 1

样例输出:

1
2

代码:

#include<iostream>
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;
    }
    
    LL e, x0, y0;
    e = exgcd(b, a % b, x0, y0);
    x = y0, y = x0 - a / b * y0;
    
    return e;
}

int main()
{
    int T;
    cin >> T;
    while(T -- )
    {
        LL n, d, a, b, x, y;
        cin >> n >> d >> x >> y;
        LL e = exgcd(d, n, a, b);
        if((y - x) % e) cout << "Impossible" << endl;
        else
        {
            a = (a * ((y - x) / e));
            n /= e;
            cout << (a % n + n) % n<< endl;  //处理负数
        }
        
    }
    
    return 0;
}

 

1301. C 循环

C 循环
在这里插入图片描述
样例输入:

3 3 2 16
3 7 2 16
7 3 2 16
3 4 2 16
0 0 0 0

样例输出:

0
2
32766
FOREVER

代码:

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long LL;

LL exgcd(LL a, LL b, LL &x, LL &y)  //拓展欧几里得算法 ax + by = d
{
    if (b == 0)  //边界情况
    {
        x = 1, y = 0;
        return a;
    }
    LL d = exgcd(b, a % b, y, x);  //y*b + x(a mod b) = d;
    y -= a / b * x;  //公式推系数(下方图一)
    return d;
}

int main()
{
    LL a, b, c, k;

    while (cin >> a >> b >> c >> k, a || b || c || k)  //逗号表达式
    {
        LL x, y;  //系数  公式:x*C - y2^k = B - A
        LL z = 1ll << k;  //为了方便,公式:x*C - y*z = B - A
        LL d = exgcd(c, z, x, y); 
        if ((b - a) % d)  //有没有解取决于B - A是否能整除d
            cout << "FOREVER" << endl;  //求余不为0,一定无解
        else  //否则一定有解
        {
            x *= (b - a) / d;  //因为等式右边原本是d,现在是(B - A), 所以等式右边x, y乘上(B-A)/d
            z /= d;  //根据公式,x = x0 + k*(b/d),这里的b就是z,此时所有的解x和x0的差,都是z的倍数
            cout << (x % z + z) % z << endl;  //x的最小非负整数解,就是x0 mod z, c++中取模可能是负数
        }
    }

    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值