欧几里得算法

直接上代码

#include <bits/stdc++.h>
using namespace std;
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}
int main()
{
    int a, b;
    cin >> a >> b;
    cout << gcd(a, b);
    return 0;
}

欧几里得算法是经典的最常用的辗转相除法求最大公因数。
下面我们思考为何辗转相除是正确的。
假设$$g = gcd(a, b), a = mg, b=ng$$
显然,m与n互素(没有共同因数),通过辗转相除,我们得到新的a1,b1

\[a1 = b = ng, b1 = a \% b = (m\%n)g = kg \]

不断取模辗转相除最终可以得到某个数变为了0,得到(g, 0)。
那为什么不会得到(2g,0)、(3g,0)呢?因为在辗转相除的过程中,最开始的系数m、n互素,所以后面每对系数都是互素的,若有不互素情况,说明g不是gcd(a,b)。(说的有点拗口,难理解可以通过更相减损进行推导模拟)

Eratosthenes素数筛

前置

1.若x是素数,那么kx(k>1)一定不是素数。
2.非素数N的因子可以分为两组[2,$ \sqrt{N} \(]和[\) \sqrt{N} $, N],每个N在[2, \(\sqrt{N}\)]的因子,一定能在[\(\sqrt{N}\), N/2]上找到对应因子。
3.\(\sum_{i=2}^n\frac1i=lnn+\gamma\)(欧拉常数)

筛法

根据第一条,筛法就是素数的倍数,而且对于某个素数x和他的倍数kx,如果\(kx < x^2\),那么kx一定被其他更小的素数筛掉过了
根据第二条,我们要得到[2, N]的素数表,我们只要在[2,\(\sqrt{N}\)]中用素数去筛掉非素数。
根据第三条,时间复杂度为\(O(\sqrt{N}lnN)\)

代码

for (int i = 2; i * i <= n; ++i)
    {
        if (!not_prime[i])
        {
            for (int j = i * i; j <= n; j += i)
                not_prime[j] = 1;
        }
    }
扩展欧几里得算法

问题

\(ax+by=gcd(a,b)\)的解(x,y)。

分析

我们知道\(gcd(a,b)=gcd(b,a\%b)\),那么让我们考虑另一个方程:\(bx_1+(a\%b)y_1=gcd(b,a\%b)\)
若能得到x1和y1,那么通过等式和a%b=a-(a/b)*b我们也能得到

\[(x,y) = (y_1,x_1-(a/b)y_1) \]

考虑辗转相除法的过程,一直进行下去会得到

\[ax_n+0y_n = gcd(a,0) = a \]

一组解便是\(xn=1,yn=0\),反推回去得到一组(x,y)。
考虑其他解(m,n)

\[a(m-x) + b(n - y) = 0 \]

同除gcd(a,b)可以得到c=a/g,d=b/g,显然c、d互素。

\[c(m-x)+d(n-y)=0 \]

由于cd互素,那么

\[m-x=kd,n-y=kc(k不定) \]

其中k不定,我们枚举k,那么

\[(m,n)=(x+b/g*k,y+a/g*k) \]

例题

洛谷 青蛙的约会

//青蛙约会
#include <bits/stdc++.h>
using namespace std;
#define LL long long
LL k_gcd(LL a, LL b, LL &x, LL &y)
{
    if (!b)
    {
        x = 1, y = 0;
        return a;
    }
    LL d = k_gcd(b, a % b, y, x);
    y -= x * (a / b);
    return d;
}
int main()
{
    LL x, y, m, n, l, a, b, c, t, k;
    //(m - n)t + lk = y - x, find positive min t
    cin >> x >> y >> m >> n >> l;
    if (m - n < 0)
        a = n - m, c = x - y;
    else
        a = m - n, c = y - x;
    b = l;
    LL g = k_gcd(a, b, t, k);
    if (c % g)
        cout << "Impossible";
    else
    {
        t = t * (c / g);
        cout << (t % (b / g) + b / g) % (b / g);
    }
    return 0;
}