数论还是比较重要的一部分,需要我们好好掌握
话不多说,直接上算法吧
这还是比较简单的一部分了吧
线性筛是什么就不多说了,顾名思义即可
先讲讲普通筛
代码如下:
memset(isprime, true, sizeof(isprime));
isprime[1] = false;
for (int i = 2; i * i <= n; i++)
for (int j = i; i * j <= n; j++)
isprime[i * j] = false;
代码很简单,时间复杂度约为 O(n⋅log2log2n)
时间还能凑合,但是遇到 n=107 级别的时候就GG了
所以才要引进线性筛这种神奇的操作……
方法? 每一个数都只被自己的最小质因子筛一次
为什么普通筛时间会爆,因为一个数可能被筛了很多次……
看看线性筛的代码(比较丑陋)
memset(isprime, true, sizeof(isprime));
int len = 0; isprime[1] = false;
for (int i = 2; i <= n; i++) {
if (isprime[i]) prime[++len] = i;
for (int j = 1; j <= len && i * prime[j] <= n; j++) {
isprime[i * prime[j]] = false;
if (i % prime[j] == 0) break;
}
}
这就是线性筛的代码了
如果你认为它只能筛素数,那就大错特错了……
在这里我们引入一些概念
积性函数:当 (a,b)=1 时,满足 f(ab)=f(a)∗f(b) ,则 f 为积性函数
完全积性函数:对于任意数对 (a,b) ,都满足 f(ab)=f(a)∗f(b) ,则 f 为完全积性函数
常见的积性函数:
欧拉函数 φ(n) 表示在 1−n 中和 n 互质的数的个数
约数个数 d(n) 表示 n 约数个数
约数和 σ(n) 表示 n 的约数和
线性筛可以解决积性函数求值的问题
我们拿欧拉函数 φ 作为例子
对于一个积性函数,我们需要考虑2种情况
1. n=pk , p 为质数
2. n=pk11×pk22×…×pkmm ( p1,k1,p2,k2…pm,km 都为正整数)
当属于第一种情况的时候,很容易得到
还是看一看具体代码比较好
memset(phi, 0, sizeof(phi));
int len = 0; phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!phi[i]) {
phi[i] = i - 1;
prime[++len] = i;
}
for (int j = 1; j <= len && i * prime[j] <= n; j++) {
int k = i * prime[j];
if (i % prime[j] == 0) {
phi[k] = phi[i] * prime[j];
break;
}
phi[k] = phi[i] * (prime[j] - 1);
}
}
和线性筛素数的代码大同小异
再举个例子,约数和 σ(n)
还是和求 φ(n) 一样的思路,分类讨论两种情况
1. n=pk
2. n=pk11×pk22×…×pkmm ( p1,k1,p2,k2…pm,km 都为正整数)
第一种情况的 σ(n) 很好求
很显然
基本所有的积性函数怎么求值都可以这样分两步求
那么,线性筛这个部分就告一段落了
欧几里得算法求最大公约数大家应该都会
时间复杂度约为 O(log2n)
引入一个定理:
对于一个不定方程 ax+by=c ,只有 (a,b)|c 的时候,方程有整数解
推论:若 (a,b)=1 ,则 ax+by=1 有解(很显然吧)
那么,扩展欧几里得就是一个可以求得这个方程的其中一组整数解的算法
方程为: ax+by=c,c=(a,b)
我们考虑使用欧几里得算法的思想,即为辗转相除法
令 a=bq+r,r=a mod b ,递归求出 bx+ry=c 的解
假设我们求出 bx+ry=c 的解为 x′,y′
将 a=bq+r 代入 ax+by=c ,则可以转化为:
void calc(int a, int b, int &x, int &y) {
if (b==0) {
x = 1, y = 0;
return;
}
int q = a / b, r = a % b;
calc(b, r, y, x);
y -= q * x;
}
代码确实比较简洁,但是写起来可能还是需要一定的思考和理解的
那么,可能你会问,这鬼东西有什么用呢?
好,那我们来看看
来引进一个十分重要的概念,叫逆元
若 ax≡1(modb) ,则称 x 是 a 关于模 b 的逆元
这个定义式其实等价于 ax+by=1
只有当 (a,b)=1 时才会有逆元,否则不存在
我们一般这么写
a 的逆元为 x≡1(modb) ,形象化说,就是 a−1≡1(modb)
那么,在模 b 意义下, a−1=x 这样可能比较容易理解
求逆元直接用扩展欧几里得即可
显然,在 [0,b) 的范围内, a 关于模 b 的逆元(如果存在的话)是唯一的
反证法即可
只求一个很容易,那么我们要求 1−n 关于模质数 p 的逆元呢?
并且 n 在 106−107 级别
我们来推导一下吧
首先, 1−1≡1(modp) (十分明显)
那么,我们假设 p=ax+b,b<x,1<x<p
把这个式子带进去,就会得到 ax+b≡0(modp)
两边同时乘上 x−1⋅b−1 ,可得
代码如下
for (inv[1] = 1, i = 2; i <= n; i++)
inv[i] = (p - p / i) * inv[p % i] % p;
逆元这个东西还是十分重要的,在一些求方案数的dp中会大量使用,特别是遇到除法的时候