数论学习笔记

质数

定义

只存在 1 1 1和它本身两个约数的数。
质数分布较稀疏,对于一个正整数 n n n,不超过 n n n的质数大约有 ln ⁡ n n \frac{\ln n}{n} nlnn个。

质数的判定

1、试除法
若一个正整数 n n n为合数,则一定存在一个 x x x,使 x ∣ n x|n xn x ≤ n x\leq\sqrt{n} xn
证明:
由质数的定义可知,一定存在一个正整数 y y y,使 x ≤ y x\leq y xy x y = n x y=n xy=n
x ≤ n x\leq\sqrt{n} xn y ≤ n y \leq \sqrt{n} yn ,则当且仅当 x = y = n x=y=\sqrt{n} x=y=n 时, x y = n x y=n xy=n才成立。故此时 x = n x=\sqrt{n} x=n
x > n x>\sqrt{n} x>n y > n y>\sqrt{n} y>n ,则 x y > n x y>n xy>n。故当 y > n y>\sqrt{n} y>n 时, x ≤ n x \leq\sqrt{n} xn
综上述,对于每个合数 n n n,一定存在 x x x,使 x ∣ n x|n xn,且 x ≤ n x\leq\sqrt{n} xn
根据此命题,我们只需扫描 [ 2 , n ] [2,\sqrt{n}] [2,n ]内所有的数,判断其是否是 n n n的约数即可。

bool prime(int n) {
  for (int i = 2; i <= n; ++i)
    if (n % i == 0) return false;
  return true;
}

时间复杂度 O ( n ) O(\sqrt{n}) O(n )

2、Eratosthenes筛法
在试除法中,我们通过枚举一个数的约数来判断其是否是质数。而筛法的思想与其相反。对于每个数,我们枚举它的倍数,易证其倍数一定为合数。对于每个合数,我们将其“筛去”,而剩下的数就是质数了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V8v7Hqb4-1685786649350)(https://tse4.mm.bing.net/th?id=OIP.i9a960r19Kg8JXlFbDhmGwAAAA&pid=Api)]
观察上图,可以发现,只需对每个质数 x x x,从 x 2 x^2 x2开始枚举倍数,并将其筛去,即可得到质数表。

void sieve(int n, bool *prime) {
  memset(prime, true, sizeof is_prime);
  prime[1] = false;
  for (int i = 2; i <= n; ++i) {
    if (!prime[i]) continue;
    for (int j = i * i; j <= n; j += i)
      prime[j] = false;
  }
}

时间复杂度为 O ( Σ p n p ) O(\Sigma_{p}\frac{n}{p}) O(Σppn) = = = O ( n log ⁡ log ⁡ n ) O(n\log\log n) O(nloglogn),效率接近线性。
3、线性筛
尽管加了许多优化,Eratosthenes筛法仍然会重复标记合数。可以这样想,如果能够确定每个合数会被哪个质数筛去,就可以在线性时间复杂度内筛除质数表。
于是维护一个 v v v数组, v i v_i vi表示 i i i的最小质因数。同时维护一个 p p p数组,记录质数表。

void sieve(int n, int &pr, int *p, int *v) {
  for (int i = 2; i <= n; ++i) {
    if (v[i] == 0) v[i] = i, p[++pr] = i;
    // 没被筛,是质数
    for (int j = 1; j <= pr; ++j) {
      int k = i * p[j];
      v[k] = p[j];
      if (i % p[j] == 0 || k > n) break; 
      // 有比p[j]更小的质因子或超出范围
    }
  }
}

质因数分解

算数基本定理(唯一分解定理)

任何一个大于1的自然数 N N N ,都可以唯一分解成有限个质数的乘积。
N = Π i = 1 m p i c i N = \Pi_{i=1}^m { p_i ^ { c_i } } N=Πi=1mpici,其中 p i p_i pi为质数且 p i − 1 < p i p_{i-1} < p_i pi1<pi c i c_i ci为正整数。
这样的分解叫做 N N N的标准分解式。

质因数分解算法

1、试除法
对于一个正整数 N N N,可以证明至多存在一个大于 N \sqrt N N 的质因数。所以只需扫描 [ 2 , N ] [2, \sqrt N] [2,N ]之间的数。对于每个被扫描的数 d d d,若 d d d为质数且 d ∣ n d|n dn,则将 d d d除尽,并统计其个数。

void decom(int n, int &m, int *p, int *c) {
  // 分解 n,分解式储存在 p[1..m] 及 c[1..m] 中
  m = 0;
  for (int i = 2, size = sqrt(n); i <= size; ++i)
    if (n % i == 0) {
      p[++m] = i, c[m] = 0;
      for (; n % i == 0; n /= i)
        ++c[m];
    }
  if (n > 1) p[++m] = n, c[m] = 1; // 特判 n 为质数的情况
}

时间复杂度为 O ( N ) O(\sqrt N) O(N )
2、短除法
当已经筛出每个数的最小质因数时,可以更加高效的分解质因数。
对于每个正整数 N N N,易证其最小质因数 v ( N ) v(N) v(N)一定在其唯一分解式中。则将 N N N除以 v ( N ) v(N) v(N)后,可迭代处理 N v ( N ) \frac{N} {v(N)} v(N)N。边界条件为 N = 1 N = 1 N=1

void decom(int n, int &m, int *p, int *c) {
  m = 0;
  for (; n != 1; n /= v[n])
    if (p[m] == v[n]) // 重复,累计个数
      ++c[m];
    else
      p[++m] = v[n], c[m] = 1;
}

约数

定义

略。。。

算数基本定理的推论

对于一个正整数 N N N
N N N的约数个数为 Π i = 1 m ( c i + 1 ) \Pi_{i=1}^m ({c_i + 1}) Πi=1m(ci+1)
N N N的约数和为 Π i = 1 m ( Σ j = 0 c i p i j ) \Pi_{i=1}^m(\Sigma_{j=0}^{c_i}p_i^j) Πi=1m(Σj=0cipij)

正约数集合

1、试除法
d ∣ N d|N dN d ≤ N d\leq\sqrt N dN ,则定有 N d ∣ N \frac{N}{d}|N dNN。即约数总是成对出现的。
因此,只需扫描 [ 1 , N ] [1,\sqrt N] [1,N ]中的数。对于每个数 d d d,判断其能否整除 N N N,若能整除,则将 d d d N d \frac{N}{d} dN同时加入 N N N的约数集合中。当 d = N d=\sqrt N d=N 时特判即可。

void calc(int n, int &m, int *d) {
  m = 0;
  for (int i = 1, size = sqrt(n); i <= size; ++i)
    if (n % i == 0) {
      d[++m] = i;
      if (i != size) d[++m] = n / i;
    }
}

时间复杂度为 O ( N ) O(\sqrt N) O(N )
推论:一个正整数 N N N的约数个数上界为 N \sqrt N N
2、迭代法
[ 1 , N ] [1,N] [1,N]中每个数的约数集合,可以使用迭代法。
扫描每个数 d d d,并将其加入其所有范围内的倍数的约数集合中。

void calc(int n, vector<int> *d) {
  for (int i = 1; i <= n; ++i)
    for (int j = i; j <= n; j += i)
      d[j].push_back(i);
}

时间复杂度为 O ( Σ i = 1 N N i ) = O ( N log ⁡ N ) O(\Sigma_{i=1}^N \frac{N}{i})=O(N\log N) O(Σi=1NiN)=O(NlogN)
:调和级数 Σ i = 1 N 1 i = ln ⁡ ( n + 1 ) + λ \Sigma_{i=1}^N \frac{1}{i}=\ln(n+1)+\lambda Σi=1Ni1=ln(n+1)+λ,其中 λ \lambda λ为欧拉常数。
推论: [ 1 , N ] [1,N] [1,N]中每个数的约数个数总和大约为 N log ⁡ N N\log N NlogN

约数和 & 约数个数和

除法分块

详见P2261 [CQOI2007]余数求和
这是Capella的题解,比较详细。

数论函数

定义

在数论上,数论函数指定义域为正整数、陪域为复数的函数。

积性函数

常见的积性函数

  1. φ ( n ) \varphi(n) φ(n):欧拉函数
  2. μ ( n ) \mu(n) μ(n):莫比乌斯函数
  3. σ k ( n ) \sigma_k(n) σk(n): Σ d ∣ n d k \Sigma_{d|n}d^k Σdndk

积性函数的性质

f f f为积性函数,则 ( a , b ) = 1 ⇒ f ( a b ) = f ( a ) f ( b ) (a,b)=1 \Rightarrow f(ab)=f(a)f(b) (a,b)=1f(ab)=f(a)f(b)

线性筛求积性函数

推荐这篇博客

欧拉函数

李煜东的《算法竞赛进阶指南》中讲解很详细,这里补充2条性质:
1、除了 N = 2 N=2 N=2 2 ∣ φ ( N ) 2|\varphi(N) 2∣φ(N)
2、当 N N N为奇数时, φ ( 2 N ) = φ ( N ) \varphi(2N) = \varphi(N) φ(2N)=φ(N)
例题
P1390 公约数的和
P2158 [SDOI2008]仪仗队

莫比乌斯函数

略。。。

同余

费马小定理

p p p为素数,则对于任意整数 a a a,有 a p ≡ a ( m o d    p ) a^p \equiv a (\mod p) apa(modp)

剩余系

欧拉定理

同余方程

乘法逆元

定义

在模 p p p意义下, a × a − 1 ≡ 1 ( m o d    p ) a \times a^{-1} \equiv 1 (\mod p) a×a11(modp) a − 1 a^{-1} a1成为 a a a的乘法逆元。

利用扩展欧几里得求解

即求解同余方程 a × x ≡ 1 ( m o d    p ) a \times x \equiv 1 (\mod p) a×x1(modp)

long long inv(long long a) {
  long long x, y;
  long long d = exgcd(a, p, x, y);
  if (d != 1) return -1; // 不存在逆元
  return (x % p + p) % p;
}

利用费马小定理求解

p p p为素数,则 a p − 1 ≡ 1 ( m o d    p ) ⇒ a p − 2 ≡ a − 1 ( m o d    p ) a^{p-1} \equiv 1 (\mod p) \Rightarrow a^{p-2} \equiv a^{-1}(\mod p) ap11(modp)ap2a1(modp)
a p − 2 a^{p-2} ap2 a a a的乘法逆元。

利用线性筛求解

由逆元的定义可知逆元为完全积性函数,故可以用线性筛求解。
对于素数的逆元,直接用费马小或扩欧,因为 n n n以内素数个数为 n ln ⁡ n \frac{n}{\ln n} lnnn,所以时间复杂度还是 O ( n ) O(n) O(n)

void sieve(int n) {
  v[1] = 1;
  for (int i = 2; i <= n; ++i) {
    if (v[i] == 0) {
      v[i] = i;
      p[++pr] = i;
      inv[i] = pow_mod(i, P - 2);
    }
    for (int j = 1, k; k = i * p[j], j <= pr && k <= n; ++j) {
      v[k] = p[j];
      inv[k] = inv[i] * inv[p[j]] % P;
      if (i % p[j] == 0) break;
    }
  }
}

线性递推

首先 1 − 1 ≡ 1 ( m o d    p ) 1^{-1} \equiv 1(\mod p) 111(modp)
对于 i i i,设 k = ⌊ p i ⌋ k=\lfloor \frac{p}{i}\rfloor k=ip r r r满足 p = k × i + r p=k\times i + r p=k×i+r 0 < r < i 0<r<i 0<r<i
易证 k × i + r ≡ 0 ( m o d    p ) k\times i + r \equiv 0 (\mod p) k×i+r0(modp)
两边同除以 i , r i,r i,r,得
k × r − 1 + i − 1 ≡ 0 ( m o d    p ) k\times r^{-1} +i^{-1}\equiv 0 (\mod p) k×r1+i10(modp)
移项,得
i − 1 ≡ k × r − 1 ( m o d    p ) i^{-1}\equiv k \times r^{-1} (\mod p) i1k×r1(modp)

inv[1] = 1;
for(int i = 2; i <= n; i++)
  inv[i] = (p - p / i) * inv[p % i] % p;

反演

略。。。

卷积

略。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值