zoukankan      html  css  js  c++  java
  • 【洛谷 1082】同余方程

    题目描述

    求关于xx的同余方程 a x equiv 1 pmod {b}ax1(modb) 的最小正整数解。

    输入格式

    一行,包含两个正整数 a,ba,b,用一个空格隔开。

    输出格式

    一个正整数 x_0x0,即最小正整数解。输入数据保证一定有解。

    输入输出样例

    输入 #1
    3 10
    输出 #1
    7

    说明/提示

    【数据范围】

    对于 40%的数据,2 ≤b≤ 1,0002b1,000;

    对于 60%的数据,2 ≤b≤ 50,000,0002b50,000,000;

    对于 100%的数据,2 ≤a, b≤ 2,000,000,0002a,b2,000,000,000。

    NOIP 2012 提高组 第二天 第一题

    题解:洛谷教会我的。题解也来自洛谷(超详细)

                  

    问题转化

    题目问的是满足 ax mod b = 1axmodb=1 的最小正整数 xx。(a,ba,b是正整数)

    但是不能暴力枚举 xx,会超时。

    把问题转化一下。观察 ax mod b = 1axmodb=1,它的实质是 ax + by = 1ax+by=1:这里 yy 是我们新引入的某个整数,并且似乎是个负数才对。这样表示是为了用扩展欧几里得算法。我们将要努力求出一组 x,yx,y 来满足这个等式。稍微再等一下——

    问题还需要转化。扩展欧几里得是用来求 ax + by = gcd(a,b)ax+by=gcd(a,b) 中的未知数的,怎么牵扯到等于 11 呢?

    原理是,方程 ax + by = max+by=m 有解的必要条件是 m mod gcd(a,b) = 0mmodgcd(a,b)=0。


    这个简单证一下。

    由最大公因数的定义,可知 aa 是 gcd(a,b)gcd(a,b) 的倍数,且 bb 是 gcd(a,b)gcd(a,b) 的倍数,

    若 x,yx,y 都是整数,就确定了 ax + byax+by 是 gcd(a,b)gcd(a,b) 的倍数,

    因为 m = ax + bym=ax+by,所以 mm 必须是 gcd(a,b)gcd(a,b) 的倍数,

    那么 m mod gcd(a,b) = 0mmodgcd(a,b)=0。


    可得出在这道题中,方程 ax + by = 1ax+by=1 的有解的必要条件是 1 mod gcd(a,b) = 01modgcd(a,b)=0。可怜的 gcd(a,b)gcd(a,b) 只能等于 11 了。这实际上就是 a,ba,b 互质。

    扩展欧几里得

    前提是知道了普通欧几里得算法(辗转相除法)。下面字母挺多,希望你耐心地慢慢地读~

    我们拿到了一组 a,ba,b。目标是求出满足 ax + by = gcd(a,b)ax+by=gcd(a,b) (①) 的整数 xx 与 yy。其中 xx 应当是满足条件的最小正整数,它会成为答案,yy 是辅助答案。

    (注意,虽然刚刚已经证明本题的 gcd(a,b)gcd(a,b) 必然等于 11,但是扩展欧几里得算法本身过程求的是 ax + by = gcd(a,b)ax+by=gcd(a,b) 的解。它正好非常适合本题,不过我们要按照它求解的过程去做)

    根据普通欧几里得算法,gcd(a,b) = gcd(b, amod b)gcd(a,b)=gcd(b,amodb) (②)。

    如果我们先前已经求出了另一组数 x_2, y_2x2,y2,它们满足 bx_2 + (a mod b)y_2 = gcd(b, a mod b)bx2+(amodb)y2=gcd(b,amodb) (③),(提示一下,这个等式与①的结构一致,系数则是根据②替换的。此处的递归形态已经有所显露,别着急

    结合②③,一定有

    bx_2 + (a mod b)y_2bx2+(amodb)y2

    =gcd(b, a mod b)=gcd(b,amodb)

    =gcd(a,b)=gcd(a,b)

    在这个“如果”实现的时候,我们的目标变成了

    求出满足 ax + by = bx_2 + (a mod b)y_2ax+by=bx2+(amodb)y2 (④)的 xx 与 yy。

    其中a,b,x_2,y_2a,b,x2,y2 都已知,x,yx,y待求。未知数比方程更多,没有唯一解。我们先求出一组必然存在的解,最后将在答案处理时转变为使正整数 xx 最小的解。

    取模运算是 a mod b = a - b×(a/b)amodb=ab×(a/b),能理解吧?

    等式④实际上是:

    ax + by = bx_2 + (a-b×(a/b))y_2ax+by=bx2+(ab×(a/b))y2

    ax + by = bx_2 + ay_2 - b × (a/b)y_2ax+by=bx2+ay2b×(a/b)y2

    ax + by = ay_2 + b(x_2-(a/b)y_2)ax+by=ay2+b(x2(a/b)y2)

    看上面这个方程,一组必然存在的解出现了!

    x = y_2, y = x_2 - (a/b)y_2x=y2,y=x2(a/b)y2 ⑤

    可见我们只要求出 x_2,y_2x2,y2,就能得出正确的x,yx,y。问题是 x_2,y_2x2,y2 怎么求。

    脑海里抛掉前面的x,yx,y,现在我们手上是 b,a mod bb,amodb 这两个系数,而目标是求出满足 bx_2 + (a mod b)y_2 = gcd(b,a mod b)bx2+(amodb)y2=gcd(b,amodb)(③)的 x_2x2 与 y_2y2,以便于待会回馈给⑤。

    等一下。

    比较于③,我再去看看原方程①:

    ax + by = gcd(a,b)ax+by=gcd(a,b) (①)

    原方程中的 aa 变成了③中的 bb,原方程中的 bb 变成了③中的 a mod bamodb 而已。

    按照上面的一模一样下来(其实都只是推导过程),我们发现,最好有 x_3,y_3x3,y3 来支撑 x_2, y_2x2,y2

    再一模一样下来,我们又需要 x_4,y_4x4,y4 来支撑 x_3, y_3x3,y3

    ……

    这个递归中 a,ba,b 不断变成 b, a mod bb,amodb,逼近最后:

    bb 将等于最早的 a,ba,b 的最大公因数,就像普通 gcdgcd 的结果。

    但我们再往下一层。此时由于 a mod b = 0amodb=0,下一层的 b = 0b=0。

    是时候直接回馈了,我们需要一组 x_n,y_nxn,yn 满足 a_nx_n + b_ny_n = gcd(a_n,b_n)anxn+bnyn=gcd(an,bn)。

    然而本层的 b_n = 0bn=0,则 gcd(a_n,b_n) = gcd(a_n,0) = a_ngcd(an,bn)=gcd(an,0)=an。那么只要等式左边取 x_n = 1xn=1,这个等式就妥妥的成立了。

    最后一层回馈的 y_nyn 建议用 00,但由于 b = 0b=0,回馈其它数值等式一定也成立。意料之外的是,这样的程序有时候会求错解。如果回馈 33,那么你将收获 7070 分的好成绩。如果回馈 -2000220002,那么你将收获 4040 分的好成绩。为什么呢?

    原因仅仅是,被回馈(却与 00 等效)的 y_nyn 在回溯时滚雪球式增长,容易数值越界。下面的代码在最后一层令 y = 7y=7,开了long long,没有出问题。

    最后一层结束后,就开始返回,直到最上层。每一层可以轻松地根据下层的 x_{k+1},y_{k+1}xk+1,yk+1 求出当前层的 x_k, y_kxk,yk

    小提示:

    上面的算法中,以辗转相除的方式向下递进的好处(目的)是缩小系数,直到出现一个解可以确定的情况。

    对扩展欧几里得也可以有不同的理解方式,比如可以设 G = gcd(a,b),然后类似地推导,每一层的等式右边都是G。

    答案处理

    一个重大遗留:我们将要求出来的 x,yx,y 仅仅保证满足 ax + by = 1ax+by=1,而 xx 不一定是最小正整数解。有可能太大,也有可能是负数。这依然可以解决,那就是,xx 批量地减去或加上 bb,能保证 ax + by = 1ax+by=1:

    ax + by = 1ax+by=1

    ax + by + k×ba - k×ba = 1ax+by+k×bak×ba=1

    a(x+kb) + (y-ka)b = 1a(x+kb)+(yka)b=1

    1.显然这并不会把方程中任何整数变成非整数。

    2.“加上或减去 bb”不会使 xx 错过任何解。可以这么理解:


    已经求出一组整数 x,yx,y 使得 ax + by = 1ax+by=1,也就是 frac{1-ax}{b} = yb1ax=y。yy 是整数,可见目前 1-ax1ax 是 bb 的倍数。

    现在想改变 xx 并使得方程仍然成立。已知 a,ba,b 互质,假若 xx 的变化量(Delta xΔx)不是 bb 的倍数,则 1-ax1ax 的变化量(-a×Delta xa×Δx)也不是 bb 的倍数,这会使得 1-ax1ax 不再是 bb 的倍数,则 yy 不是整数了。

    仅当 xx 的变化量是 bb 的倍数时,1-ax1ax 能保持自己是 bb 的倍数,此时就出现新的解了。


    因此到最后,如果 xx 太小就不断加 bb 直到大于等于 00,太大则一直减 bb,直到最小正整数解。直接求模实现更快。

    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<cmath>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    using namespace std;
    typedef long long ll;
    ll x,y,x0,y00;
    void exgcd(ll a,ll b){
        if(b==0){
            x=1; y=0;
            return ;
        }
        exgcd(b,a%b);
        x0=x; y00=y;
        x=y00; y=x0-(a/b)*y00;
    }
    int n,m;
    int main(){
        //freopen("1082.in","r",stdin);
        //freopen("1082.out","w",stdout);
        scanf("%lld %lld",&n,&m); exgcd(n,m);
        //while(x<0) x+=m; x%=m;
        printf("%lld",(x+m)%m); 
        return 0;
    }
  • 相关阅读:
    cocos2d-x类型转换(CCstring int string char UTF-8互转)
    cocos2d-x 实心圆
    CCLabelTTF的多行显示(仅限中文)
    cocos2d-x 弹窗
    cocos2d-x 画线
    cocos2d-x 利用CCLabelTTF制作文字描边与阴影效果的实现方法
    cocos2d-x 背景色修改
    cocos2d-x 中文乱码问题
    HelloWorld.exe 中的 0x0f9265f6 (libcocos2d.dll) 处有未经处理的异常: 0xC0000005: 读取位置 0x00000038 时发生访问冲突
    error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
  • 原文地址:https://www.cnblogs.com/wuhu-JJJ/p/11311436.html
Copyright © 2011-2022 走看看