Loading

2021 SDSC D1 基础数论

题单中的一小部分

Part 1 初月(Easy Mode)

质数

暴力

\(2\) 枚举到 \(⌊\sqrt n⌋\) 试试是否可以整除就可以啦。

显然时间复杂度是 \(O(\sqrt n)\) 的。

埃氏筛

刚才的试除法让我们知道,找到一个数的因数是很难的。

但是找到一个数的倍数就很简单了。

因此我们可以从 \([2, n]\) 中依次枚举,每个数的倍数必然不是质数
复杂度?

\(F(n)=\frac{n}{2}+\frac{n}{3}+⋯+\frac{n}{n}=O(n \log n)\)

实际上底数是 \(e\),因此调和级数的常数很小,\(1s\) 跑得过。

由算术基本定理可知,只需要枚举质数的倍数即可。

枚举 \(p\) 的倍数时,只需从 \(p^2\) 开始枚举。

时间复杂度 \(O(n \log \log n)\)

线性筛

刚才的做法已经很优秀了,可是为什么还不是线性呢?

原因仍然在于,一个数字可能被筛掉多次。

例如 \(n=15\),那么 \(12\) 这个数会在 \(p=2\)\(p=3\) 时各被筛一次。

想要做到线性,就必须让一个数只被一个质数筛掉,我们选择这个数为它最小的质因数。

首先从 \(2\) 到 \(n\) 枚举自然数 \(q\)(不一定是质数),再从小到大枚举比 \(q\) 小的质数 \(p\),筛掉 \(pq\);如果 \(q\)\(p\) 的倍数,就跳出内层循环。

void primes(int n)
{
    memset(v, 0, sizeof(v)); //清空标记数组
    m = 0;                   //质数个数
    for (int i = 2; i <= n; i++)
    {
        if (!v[i])                    //未被标记,i为质数
            v[i] = i, prime[++m] = i; //记录
        for (int j = 1; j <= m; j++)
        {
            if (prime[j] > v[i] || prime[j] > n / i)
                break;                  //i有更小的质因子,或者超出n的范围
            v[i * prime[j]] = prime[j]; //prime[j]为合数 i*prime[j]的最小质因子
        }
    }
}

根据“高斯素数定理”,如果需要粗略的估计 \([2, n]\) 内的素数个数,可以直接硬点它为 \(\frac{n}{\ln n}\)

GCD & LCM

GCD,Greatest Common Divisor 的缩写,意为最大公约数

LCM,Least Common Multiple 的缩写,意为最小公倍数

\(\gcd(a, b)=\gcd(a−b, b)= \gcd(b, a\text{ mod } b)\)

注意到每次取模至少减半,因此复杂度为 \(O(\log n)\)

而求 LCM 只需求出 GCD ,因为:

\(\gcd(a, b) \times \text{lcm}(a, b)=a \times b\)

进制转换

\(n\)\(a\) 进制数转化为 \(b\) 进制数,最终为 \(m\) 位。

将给定的 \(a\) 进制数从高位向低位扫描,每次将当前结果乘以 \(a\),并加上当前位的系数,再从低位向高位进位即可。

复杂度 \(O(nm)\),用多项式科技可以优化,但大多数情况下没必要。

瓶颈在于 \(n\) 次乘 \(a\) 和进位,每次都是 \(O(m)\) 的。

如果 \(a, b\) 比较小,可以通过压位的方式进一步优化。

即,先将原数转化为 \(a^p\) 进制,再转化为 \(b^q\) 进制,可以将复杂度降为 \(O(\frac{nm}{pq})\),为了计算方便,以 \(a^p, b^q\) 不超过 \(2×10^9\) 为妙。

Part 2 三日月(Normal Mode)

同余

若对于给定的正整数 \(m\),有正整数 \(a, b\),满足 \(a=km+b, k∈Z\),则称 \(a, b\)\(m\) 同余,用 \(a≡b (\mod m)\) 表示。

\(m\) 意义下,整数集只剩下了 \([0, m)\)\(m\) 个整数,其他整数都应该加上或减去若干个 \(m\) 来调整到这个区间内。

注意到负整数 \(x\) 对应的数是 \(km+x\),而不是 \(km−x\)

顺便提一句,\(a \mod b=a−⌊\frac ab⌋ \times b\),这一点后面还会提到。

不难证明,模意义下的加法、减法、乘法与一般意义下相同,各运算规律依然满足。

但是很可惜,除法与它们不同。

考虑一般意义下的除法,\(a÷b\) 等价于 \(a \cdot b^{-1}\),而 \(b^{−1}\) 满足 \(b\cdot b^{−1}=1\) 这一性质,从而进行乘法的逆运算。

换句话说,假如我们能对任意 \(b\) 找到 \(b^{−1}\),使得\(b\cdot b^{−1}≡1(\mod m)\),不就能实现模 \(m\) 意义下的除法了吗?

逆元

因此,我们需要引入逆元的概念。

大多数情况下,当我们需要在模意义下做除法时,模数都是质数。

此时 \((0, m)\) 中每个整数都有唯一的逆元。

inv[0] = inv[1] = 1;
for (register int i(2); i <= n; i++)
    inv[i] = (1ll * (mod - mod / i) * inv[mod % i]) % mod;

费马小定理

若整数 \(a∈(0, m)\)\(m\) 为质数,则 \(a^{m−1}≡1 (\mod m)\)

有了它,我们就能求出 \(a\) 的逆元了:

\(a\cdot a^{m−2}=a^{m−1}≡1 (\mod m)\),所以 \(a^{−1}≡a^{m−2} (\mod m)\)

绝大多数情况下逆元都是这样求的。

扩展欧几里得算法 & 裴蜀定理

\(x=a^{−1}\),那么有 \(a\cdot x≡1 (\mod m)\)

将它写成一般形式,得到 \(x, y\) 的不定方程 \(ax−my=1\)

如果能得到一组整数解 \((x_0, y_0)\),我们就可以得到它的通解:

\(x=x_0+km, y=y_0+ka, k∈Z\),选择合适的 \(k\) 即可得到 \(x\)

现在问题在于,方程是否有解,以及如何找到一组特解。

必要性很好证明,而下面要讲的扩展欧几里得算法则通过构造证明了充分性。

\(d=\gcd(a, b)\),扩展欧几里得算法可以给出不定方程 \(ax+by=d\) 的一组整数特解 \((x_0, y_0)\)

类比刚才的情况,通解有 \(x=x_0+k\cdot \frac bd, y=y_0−k\cdot \frac ad, k∈Z\)

对于 \(ax+by=c, d|c\) 的情况,可以先解 \(ax+by=d\),再将 \(x, y\) 都乘以 \(\frac cd\)

考虑之前欧几里得算法求 \(\gcd\) 的过程,我们从 \(\gcd(a, b)\) 递归到 \(\gcd(b, a \mod b)\)

\(a^′=b, b^′=a \mod b\),那么如果我们有了不定方程 \(a^′x+b^′y=d\) 的一组整数解 \((x^′, y^′)\),能否推出 \(ax+by=d\) 的一组整数解 \((x, y)\) 呢?

\(b^′=a \mod b=a−⌊a/b⌋ \cdot b\),代回 \(a^′x^′+b^′y^′=d\) 得到 \(bx^′+(a−⌊a/b⌋\cdot b)y^′=d\),整理得$ ay′+b(x′−⌊a/b⌋\cdot y^′)=d$。

也就是说,令 \(x=y^′, y=x^′−⌊a/b⌋\cdot y^′\) 即可完成递归的回推

裴蜀定理:对于给定的正整数 \(a, b\),设 \(d=\gcd(a, b)\),则关于 \(x, y\) 的不定方程 \(ax+by=c\) 存在整数解,当且仅当 \(d|c\)

欧拉函数

欧拉函数:定义域为正整数的函数 \(φ(x)\),表示 \([1, x]\) 中与 \(x\) 互质的正整数个数。

\(x=∏p_i^{e_i}\),则 \(φ(x)=x\cdot ∏\frac{p_i−1}{p_i}\),证明可以考虑每次划掉 \(x\) 所有质因子的倍数,剩下的就是与 \(x\) 互质的。

欧拉函数可以线性筛,只需判断当前质因子是否出现过,以选择乘以 \(p\) 还是 \(p−1\)


int prime[N], phi[N];
bool isprime[N];

void pre()
{
    ll cnt = 0;
    isprime[1] = 1;
    phi[1] = 1;
    for (register int i(2); i <= n; ++i)
    {
        if (!isprime[i])
        {
            prime[++cnt] = i;
            phi[i] = i - 1;
        }
        for (register int j(1); j <= cnt and i * prime[j] <= n; ++j)
        {
            isprime[i * prime[j]] = 1;
            if (i % prime[j])
                phi[i * prime[j]] = phi[i] * phi[prime[j]];
            else
            {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
        }
    }
}

欧拉定理

那么为什么要讲欧拉函数呢?就是为了引出欧拉定理!

\(a, m\) 互质,则 \(a^{φ(m)}≡1 (\mod m)\)

\([1, m]\) 中与 \(m\) 互质的数列出来,设为 \(b_1, b_2, ⋯, b_{φ(m)}\)

\(c_i=a\cdot b_i \text{mod }m\),可以证明 \(c_i\) 两两模 \(m\) 不同余,且任意 \(c_i\)\(m\) 互质。

那么 \(c\) 其实是 \(b\) 重排的结果,因为与 \(m\) 互质的数只有 \(φ(m)\) 个。

因此 \(∏ b_i≡∏ c_i≡a^{φ(m)}\cdot ∏ b_i (\mod m)\),得证。

前面提到的费马小定理其实是欧拉定理的子定理。

扩展欧拉定理

刚才的欧拉定理只适用于 \(a, m\) 互质的情况

对于不互质的情况,此处不证明地给出扩展欧拉定理:

\(c<φ(m),则 a^c≡a^c (\mod m)\)

\(c≥φ(m),则 a^c≡a^{[c \mod φ(m)]+φ(m)} (\mod m)\)

证明比较复杂,有兴趣的同学可以自行查阅或浏览这篇博客

Part 3 上弦月(Hard Mode)

中国剩余定理

BSGS 算法

整除分块(数论分块)

积性函数

莫比乌斯反演

posted @ 2021-08-29 18:17  EdisonBa  阅读(72)  评论(0编辑  收藏  举报