基础数论
基础数论
前置芝士:
等比数列求和: \(S_n=a_0\frac{1-q^n}{1-q}\)
质数与约数:
整除与约数
设 \(n\) 为非负整数,\(d\) 为正整数,若 \(\frac{n}{d}\) 为整数,则称 \(d\) 整除 \(n\),记为\(d\mid n\)。此时,则称 \(d\) 是 \(n\) 的约数,或因数、因子;称 \(n\) 为 \(d\) 的倍数。
质数与合数的定义:
对于 \(n \geq 2\) ,若 \(\forall 1<i<n,i\nmid n\) ,则称 \(n\) 为质数,否则 \(n\) 为合数。
质数判定:
试除法:
单次时间复杂度 \(O(\sqrt n\ )\)
代码
bool is_prime(int n) {
if(n < 2)
return false;
for(int i=2; i<= sqrt(n); ++i) {
if(n % i == 0)
return false;
}
return true;
}
质数筛法一:
从 \(2\) 到 \(n\) 枚举整数 \(i\),标记大于 \(i\) 且不大于 \(n\) 的 \(i\) 的倍数。枚举到 \(i\) 时,若 \(i\) 没有被标记过,则 \(i\) 为质数。
时间复杂度 \(O(\sum_{i=1}^n \frac{n}{i})=O(n\log n)\)
代码
void found_prime() {
memset(vis, 0, sizeof(vis));
vis[0] = 1; vis[1] = 1; // 特殊处理 0 和 1
for(int i=2; i<=n; ++i) {
for(int j=i*2; j<=n; j+=i)
vis[j] = 1; // 标记合数
}
}
埃氏筛:
只需要枚举质数的倍数就可以标记到所有合数了。实际上对于每个质数 \(p\) 而言,小于\(p^2\) 的 \(p\) 的倍数在扫描到更小的质数时就已经被标记过了。
从 \(2\) 到 \(n\) 枚举整数 \(i\),若 \(i\) 是质数,则把 \(i^2\) , \((i+1) \times i\), \(\cdots\), \(\lfloor \frac{n}{i}\rfloor \times i\) 标记为合数。枚举到 \(i\) 时,若 \(i\) 没有被标记过,则 \(i\) 为质数。
时间复杂度 \(O(\sum_{pr[i]\leq n}\frac{n}{pr[i]})=O(n\log\log n)\)
代码
void found_prime() {
memset(vis, 0, sizeof(vis));
vis[0] = 1; vis[1] = 1; // 特殊处理 0 和 1
for(int i=2; i<=n; ++i) {
if(!vis[i]) { // i 为质数
for(int j=i*i; j<=n; j+=i)
vis[j] = 1; // 标记合数
}
}
}
线性筛:
用每一个合数的最小质因子来标记这个合数。时间复杂度 \(O(n)\)
代码
int pr[N],cnt;
bool vis[N];
void init(){
int n=1e5+50;
for(int i=2;i<=n;i++){
if(!vis[i]){pr[++cnt]=i;}
for(int j=1;j<=cnt&&i*pr[j]<=n;j++){
vis[i*pr[j]]=1;
if(i%pr[j]==0)break;
}
}
}
区间筛:(埃氏筛)
代码
const int maxn = 1e6+10;
typedef long long LL;
bool is_prime[maxn]; //标记a~b范围内的质数
bool is_prime_small[maxn]; //标记 1~sqrt(n)范围内的所有质数
LL a, b;
//对区间[a, b]内的整数执行筛法,is_prime[i-a]=true表示i是素数
void found_prime()
{
for(LL i=0; i*i<=b; ++i)
is_prime_small[i] = true;
is_prime_small[1] = false;
for(LL i=0; i<=b-a; ++i)
is_prime[i] = true;
for(LL i=2; i*i<=b; ++i) {
if(is_prime_small[i]) { // i是质数
for(LL j=i*i; j*j<=b; j+=i) // 标记 sqrt(b) 以内的i的倍数
is_prime_small[j] = false;
for(LL j = max(2LL, (a+i-1)/i) * i; j<=b; j+=i) //标记[a, b]中i的倍数
is_prime[j-a] = false;
}
}
}
算术基本定理:
任何一个大于 1 的正整数都能唯一分解为若干个质数的乘积。
设 \(n\geq2\) 为整数,有唯一的分解式:
其中 \(c_i\) 都是正整数,\(p_i\) 都是质数,且满足 \(p_1\leq p_2\leq...\leq p_m\)
根据算术基本的定理,对于任意一个大于 \(2\) 的整数 \(n\),他的正约数集合可以写作:
推论一: \(n\) 的正约数个数为:
推论二: \(n\) 的所有正约数之和为:
阶乘分解:
对于一个数 \(n\) 求 \(n!\) 的质因数分解:
考虑对于一个质数 \(p_i\) 求 \(n!\) 中包含多少个 \(p_i\)答案是 \(\sum_{i=1}^{\lfloor\log_pn\rfloor}\lfloor\frac{n}{p^i}\rfloor\)对于每一个质数都用如上方式共有 \(\frac{n}{\ln n}\) 个数,每次处理 \(\log n\) 的复杂度,总复杂度 \(O(n)\) 。
求解正约数集合:
试除法 求 \(n\) 的正约数:
时间复杂度 \(O(\sqrt n )\)
代码
int divisor[10010], cnt = 0;
for(int i=1; i<=sqrt(n); ++i) {
if(n % i == 0) {
divisor[++cnt] = i;
if(i != n/i) divisor[++cnt] = n/i;
}
}
推论:一个整数 \(n\)的正约数个数最多不超过 \(2\times \sqrt n\) 个
倍数法 求\(1\sim n\)的正约数:
先枚举 \(1 \sim n\) 中的每一个数作为约数 \(d\),再在 \(1\sim n\) 寻找 \(d\) 的倍数即可。时间复杂度 \(O(n+\frac{n}{2}+\cdots+\frac{n}{n})=O(n\log n)\)
代码
vecotr<int> divisor[500010];
for(int i=1; i<=n; ++i) { // 先枚举约数 i
for(int j=1; j<=n/i; ++j) //枚举 i 在 1~n 范围内的倍数 i × j
divisor[i*j].push_back(i); // i 是 i × j 的约数
}
推论:\(1\sim n\) 的约数个数总和大约为 \(n\log n\) 个。
约数研究:
设 \(f(x)\) 为 \(x\) 的约数个数,求 \(\sum_{i=1}^nf(i)\)
可以枚举每一个 \(i\in[1,n]\) ,\(1\sim n\) 中含有 \(\lfloor\frac{n}{i}\rfloor\) 个约数 \(i\) 。所以答案为 \(\sum_{i=1}^n\lfloor\frac{n}{i}\rfloor\)
\({\rm gcd}\) :
\(\forall a,b\in \mathbb{N},d\in \mathbb{N^{*}}\) 若 \(d\mid a\ \&\ d\mid b\) 则称 \(d\) 为 \(a,b\) 的公约数。
\(a,b\) 的公约数中最大的称为 \(a,b\) 的最大公约数,记为 \(\gcd(a,b)\)。
性质:
- \(\gcd(a,b)=\gcd(b,a)\)
- 对于 $\forall a,b\in \mathbb{N} $ 若 \({\rm gcd}(a,b)=1\) 则称 \(a,b\) 互质。
- 由于任何正整数都是 \(0\) 和 \(0\) 的公约数,故 \(\gcd (0,0)\) 不存在。
- 对于 \(\forall a\in \mathbb{N^{*}}\) ,有 \(\gcd(a,a)=a,\gcd(a,0)=a\) 。
- \(\forall k\in \mathbb{Z}\),有 \(\gcd(k\times a,k\times b)=k\times \gcd(a,b)\) 。
更相减损术:
\(\forall a,b\in \mathbb{N^{*}}\ \&\ a\geq b\) 有 \(\gcd(a,b)=\gcd(b,a-b)=\gcd(a,a-b)\)
\(\forall d\mid a\ \&\ d\mid b\),有 \(a=k_1\times d,b=k_2\times d\) ,则 \((a-b)=(k_1-k_2)\times d\) ,所以 \(d\) 也是 \(b,a-b\) 的公约数,所以 \(\gcd(a,b)=\gcd(b,a-b)\) 。
辗转相除法:
\(\forall a,b\in \mathbb{N^{*}}\ \&\ b!=0\) 有 \(\gcd(a,b)=\gcd(b,a\bmod b)\)
- 若 \(a<b\) ,则 \(\gcd(b,a\bmod b)=gcd(b,a)=gcd(a,b)\)
- 若 \(a\geq b\) ,设 \(a=q\times b+r\) ,其中 \(0\leq r <b\) 。\(r=a\bmod b\) 。有 \(a=k_1\times d,q\times b=k_2\times d\) ,则 \(r=(a-q\times b)=(k_1-k_2)\times d\) ,因此 \(d\) ,也是 \(b,r\) 的公约数,故 \(\gcd(a,b)=\gcd(b,a\bmod b)\)
代码:
代码
int gcd(int a, int b) {
while(b > 0 ) {
int x = a % b;
a = b;
b = x;
}
return a;
}
递归:
代码
int gcd(int a, int b) {
return b ? gcd(b, a%b) : a;
}
最大复杂度 \(O(\log(a+b))\)
\({\rm lcm}\):
\(\forall a,b\in\mathbb{N^{*}},m\in \mathbb{N}\) 若 \(a\mid m\ \&\ b\mid m\),则称 \(m\) 为 \(a\) 和 \(b\) 的公倍数。
\(a,b\) 的公倍数中最小的称为 \(a,b\) 的最小公倍数,记为 \({\rm lcm}(a,b)\) 。$${\rm lcm}(a,b)=\frac{a\times b}{\gcd(a,b)}$$ 实际代码中: \({\rm lcm(a,b)=\frac{a}{\gcd(a,b)}\times b}\)
gcd与lcm:
给出某两个整数 \(a\) 和 \(b\) (\(a\leq b\))的最大公约数 \(GCD\) 和最小公倍数 \(LCM\) ,请找出满足的 \(a\) 和 \(b\) ,使得 \(b-a\) 的值最小。
\(\because LCM=\frac{a\times b}{GCD}\)\(\therefore \frac{a\times b}{GCD^2}=\frac{LCM}{GCD} \Rightarrow (\frac{a}{GCD})^2\leq \frac{LCM}{GCD}\)\(\therefore \frac{a}{GCD}\leq \sqrt{\frac{LCM}{GCD}}\)设 \(a'=\frac{a}{GCD}\) ,从 \(\sqrt{\frac{LCM}{GCD}}\sim 1\) 来枚举 \(a'\) ,当 \(a'\mid \frac{LCM}{GCD}\&\& \gcd(a',LCM/GCD/a')=1\) 时停止枚举。答案即为 \(a=a'\times GCD,b=\frac{LCM}{a'}\)
CF1152C:
给定两个正整数 \(a,b\) ,找到非负整数 \(k\) 使 \(a+k\) 与 \(b+k\) 的最小公倍数最小,如有多解输出最小的 \(k\) 。
设 \(a>b\) 显然 \({\rm lcm}(a+k,b+k)=\frac{(a+k)(b+k)}{\gcd(a+k,b+k)}\)由辗转相除法可知 \(\gcd(a,b)=\gcd(b,a-b)\)所以 \({\rm lcm}(a+k,b+k)=\frac{(a+k)(b+k)}{\gcd(b+k,a-b)}\)所以可以枚举 \(a-b\) 的因子 \(w\) ,若 \(b\%w=0\) 则 \(k=0\) ,否则 \(k=(\lfloor\frac{b}{w}\rfloor+1)w-b\)
整除分块:
已知 \(f(n) =\sum\limits_{i = 1 }^{n}\left\lfloor\frac{n}{i}\right\rfloor\),给定 \(n\),求 \(f(n)\) 的值。
固然可以 \(O(n)\) 暴力,但显然会\(TLE\)。
计算一下前几项的值之后可以发现\(\left \lfloor \frac{n}{i} \right \rfloor\) 的取值在连续的一段区间内是相同的,那么就可以将其分为若干块分别进行计算。
先让 \(l\) 为区间的左端点,那么这块的值都为 \(k = \left\lfloor\frac{n}{l}\right\rfloor\) , \(r=max(i)=\left\lfloor\frac{n}{k}\right\rfloor\)。将 \(k\) 代入,得到 \(r=\left\lfloor\frac{n}{\left\lfloor\frac{n}{l}\right\rfloor}\right\rfloor\)。这样每一块的左右端点都能用确定的式子得到了。这样分块的值就为单值 $\times $ 区间长度,即 \(k\times (r-l+1)\) 。
模板代码
ll division_block(ll n){
ll res = 0;
for(ll l = 1, r; l <= n; l = r + 1){
r = n / (n / l);
res += n / l * (r - l + 1);
}
return res;
}
此外,上取整的数论分块也是可做的。也就是每次对于 \(l\),要求出 \(\lceil\frac{n}{l}\rceil=\lceil\frac{n}{r}\rceil\) 的最大的 \(r\)。
令 \(k=\lceil\frac{n}{l}\rceil\),\(\frac{n}{r}>k-1\Leftrightarrow r(k-1)<n \Leftrightarrow r<\frac{n}{k-1}\Leftrightarrow r\leq \lfloor\frac{n-1}{k-1}\rfloor\)
高维数论分块:
求
每次 \(r=\min_{i=1}^c\{\lfloor\frac{n_i}{\lfloor\frac{n_i}{l}\rfloor}\rfloor\}\) 即可,复杂度是加和不是乘积,所以仍然是 \(O(\sqrt n)\) 的。
欧拉函数:
定义:
欧拉函数是指 \(1 \sim N\) 中与 \(N\) 互质的数的个数,记为 \(\varphi(N)\)。即
特别的 \(\varphi(1)=1\)
性质:
1.若 \(x\) 为质数, \(\varphi(x)=x-1\)。
质数除了他本身都与他互质。
2.\(\forall n>1,1\sim n\) 中与 \(n\) 互质的数的和为 \(n\times \varphi(n)/2\) 。
因为 \({\rm gcd}(n,x)=={\rm gcd}(n,n-x)\) ,所以与 \(x\) 不互质的数 \(x,n-x\) 成对出现,平均值为 \(\frac{n}{2}\) ,所以与 \(n\) 互质的数的平均值也是 \(\frac{n}{2}\) ,而这样的数共有 \(\varphi(n)\) 个,故得性质2。
3.若 \(x=p^k(p为质数)\) ,则 \(\varphi(x)=(p-1)\times p^{k-1}\)。
发现所有 \(p\) 的倍数都与 \(x\) 不互质,其他数都与 \(x\) 互质,而 \(p\) 的倍数共有 \(p^{k-1}\) 个(包括 \(x\) )。
故\(\varphi(x)=p^k-p^{k-1}=(p-1)\times p^{k-1}\)
4.若 \(p,q\) 互质,则 \(\varphi(p\times q)=\varphi(p)\times \varphi(q)\) ,即欧拉函数为积性函数。
如果 \(a\) 与 \(p\) 互质 \((a<p)\) , \(b\) 与 \(p\) 互质 \((b<q)\) , \(c\) 与 \(pq\) 互质 \((c<pq)\) ,则 \(c\) 与数对 \((a,b)\) 是一一对应的。
符合条件的 \(a\) 有 \(\varphi(p)\) 种, \(b\) 有 \(\varphi(q)\) 种, 则所对应的 \((a,b)\) 数对有 \(\varphi(p)\varphi(q)\) 种。而符合条件的 \(c\) 有 \(\varphi(pq)\) 种。
所以 \(\varphi(pq)=\varphi(p)\times \varphi(q)\)
5.对于一正整数 \(x=p_1^{c_1}\times p_2^{c_2}\times ...\times p_n^{c_n}\) 有
证明:
\[\varphi(x)=\prod_{i=1}^n\varphi(p_i^{c_i})=\prod_{i=1}^n p_i^{c_i-1}\times (p_i-1)=\prod_{i=1}^np_i^{c_i}\times(1-\frac{1}{p_i})=x\prod_{i=1}^n(1-\frac{1}{p_i}) \]
6.若 \(p\) 为 \(x\) 的质因数,则 \(\varphi(x\times p)=\varphi(x)\times p\)
\[\varphi(x\times p)=x\times \prod_{i=1}^n(1-\frac{1}{p_i})\times p=\varphi(x)\times p\\(p\in [p_i]) \]
7.若质数 \(p\) 不是 \(x\) 的因数,则 \(\varphi(x\times p)=\varphi(x)\times (p-1)\)
\[\varphi(x\times p)=\varphi(x)\times \varphi(p)=\varphi(x)\times(p-1) \]
设 \(f(n)=\sum_{d\mid n}\varphi(d)\)
\(\therefore f(a\times b)=\sum_{d\mid ab}\varphi(d)\)
\(当{\rm gcd}(a,b)=1时:\) \({\rm gcd}(\forall d_i\mid a,\forall d_i\mid b )=1\)
\(\therefore \sum_{d\mid ab}\varphi(d)=\sum_{d\mid a}\varphi(d)\times \sum_{d\mid b}\varphi(d)\)
即 \(f(a\times b)=f(a)\times f(b)\)
\(\therefore f(p^m)=\sum_{f\mid p^m}\varphi(d)=\sum_{i=0}^m\varphi(p^i)=1+\sum_{i=0}^{m-1}(p-1)\times p^i(p为质数)\)\(\ \ \ \ \ \ \ \ \ \ \ \ \ \ =1+(p-1)\times (1+p+\cdots+p^{m-1})\)\(\ \ \ \ \ \ \ \ \ \ \ \ \ \ =1+(p-1)\times \frac{1-p^m}{1-p}=p^m\)
\(\therefore f(p^m)=p^m\)
\(\therefore f(n)=f(\prod_{i=1}^np_i^{c_i})=\prod_{i=1}^nf(p_i^{c_i})=\prod_{i=1}^np_i^{c_i}=n\)
\(\therefore \sum_{d\mid n}\varphi(d)=n\)
9.对于 \(n>2\),有 \(2|\varphi(n)\)。
设 \(n\) 的唯一分解是 \(\prod p_i^{c_i}\)。有 \(\varphi(n)=\prod p_i^{c_i-1}(p_i-1)\)。
考虑若 \(p_i\) 中有除了 \(2\) 之外的质数,则 \(p_i-1\) 一定是偶数,那 \(\varphi(n)\) 就一定是偶数。
否则若 \(n=2^k,k>1\),有 \(\varphi(n)=2^{k-1}\),因为 \(k>1\) 所以 \(2|2^{k-1}=\varphi(n)\)。
代码:
1.求解单个欧拉函数:利用性质 \(5\) ,时间复杂度 \(O(\sqrt n)\) 。
代码
int phi(int n) {
int ans = n;
int t = sqrt(n);
for(int i=2; i<=t; ++i) {
if(n%i == 0)
ans = ans/i*(i-1);
while(n%i == 0) n /= i;
}
if(n > 1) ans = ans/n/(n-1);
return ans;
}
- 埃氏筛求 \(1\sim n\)的欧拉函数值,时间复杂度 \(O(n \log n)\)
代码
void found_euler(int n) {
for(int i=1; i<=n; ++i) phi[i] = i;
for(int i=2; i<=n; ++i) {
if(phi[i] == i) { // i为质数
for(int j=i; j<=n; j+=i) // 给包含质因子i的数字,乘上 (1-1/i)
phi[j] = phi[j]/i*(i-1);
}
}
}
3.欧拉筛求 \(1\sim n\) 的欧拉函数值,时间复杂度 \(O(n)\)
代码
int phi[N],pr[N],cnt;
bool vis[N];
void init(){
int n=1e5+50;
phi[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){pr[++cnt]=i,phi[i]=i-1;}
for(int j=1;j<=cnt&&pr[j]<=n/i;j++){
vis[i*pr[j]]=1;
phi[i*pr[j]]=phi[i]*((i%pr[j])?pr[j]-1:pr[j]);
if(i%pr[j]==0)break;
}
}
}
GCD SUM
求
原式可化为
\[\begin{array}{l} =\sum_{i=1}^n\sum_{j=1}^n\sum_{d\mid\gcd(i,j)}\varphi(d)\\ =\sum_{i=1}^n\sum_{j=1}^n\sum_{d=1}^n\varphi(d)[d\mid i][d\mid j]\\ =\sum_{d=1}^n\varphi(d)\sum_{i=1}^n\sum_{j=1}^n[d\mid i][d\mid j]\\ =\sum_{d=1}^n\varphi(d)\lfloor\frac{n}{d}\rfloor^2\end{array} \]
GCD:
给定正整数 \(n\) 求 \(1\leq x,y\leq n\&\& \gcd(x,y)\in prime\) 的数对有多少。
\[\begin{array}{l} =\sum_{p\in prime}\sum_{i=1}^n\sum{j=1}^n[gcd(i,j)=p]\\ =\sum_{p\in prime}\sum_{i=1}^{\lfloor\frac{n}{p}\rfloor}\sum{j=1}^{\lfloor\frac{n}{p}\rfloor}[gcd(i,j)=1]\\ =\sum_{p\in prime}(\sum_{i=1}^{\lfloor\frac{n}{p}\rfloor}(2\times\sum{j=1}^i[gcd(i,j)=1])-1)\\ =\sum_{p\in prime}(2\sum_{i=1}^{\lfloor\frac{n}{p}\rfloor}\varphi(i)-1)\end{array} \]
Longge 的问题
求
\[\begin{array}{l} =\sum_{i=1}^n\gcd(i,n)\\ =\sum_{d\mid n}d\times\sum_{i=1}^n[\gcd(i,n)=d]\\ =\sum_{d\mid n}d\times\sum{i=1}^{\lfloor\frac{n}{d}\rfloor}[\gcd(i,\frac{n}{d})=1]\\ =\sum_{d\mid n}d\times \varphi(\frac{n}{d})\end{array} \]
模运算与逆元:
取模定义:
取模基本性质:
设 \(a_0=a\bmod n,b_0=b\bmod n\)
- \((a+b)\bmod n=((a\bmod n)+(b\bmod n))\bmod n\)
\(a+b\equiv a_0+b_0(\bmod n)\)
-
\((a\times b)\bmod n=((a\bmod n)\times (b\bmod n))\bmod n\)\(a\times b\equiv a_0\times b_0(\bmod n)\)
-
对于任意正整数 \(k\) ,有 \(a\bmod n=(a\bmod kn)\bmod n\)
-
若 \(k \mid a\),有 \(\frac{a}{k}\ mod\ n=\frac{a\ \bmod \ kn}{k}\)
设 \(\frac{a}{k}=x\) ,
\[x-\lfloor\frac{x}{n}\rfloor\times n=\frac{xk-\lfloor\frac{xk}{kn}\rfloor\times kn}{k} \]
同余:
若整数 \(a,b\) 除以正整数 \(m\) 的余数相等,则称 \(a,b\) 模 \(m\) 同余,记为 \(a\equiv b (\bmod m)\)
逆元:
设 \(a\) 为整数,\(n\) 为正整数,若整数 \(b\) 满足,\(ab\equiv 1(\bmod n)\),则称 \(b\) 为 \(a\) 模 \(n\) 的逆元。
- 当且仅当 \(\gcd(a,n)=1\) 时, \(a\) 模 \(n\) 的逆元存在。
- 如果 \(b_1,b_2\) 为 \(a\) 模 \(n\) 的逆元,则必有 \(b_1\equiv b_2(\bmod n)\) ,即 \(a\) 模 \(n\) 的逆元在模 \(n\) 意义下唯一。
由于 \(a\) 的逆元唯一,可记为 \(a^{-1}\) 或 \(\frac{1}{n}\) 。可以定义 \(\frac{1}{a}\bmod n\)为 \(a\) 模 \(n\) 的逆元中绝对值最小的数,并取与 \(a\) 相同的符号。
费马小定理:
对于质数 \(p\) 和任意整数 \(a\) ,若 \({\rm gcd}(a,p)=1\) ,则 \(a^p\equiv a (\bmod p)\)
设有数列 \(S=\{1,2,3,\cdots,p-1\},S\bmod p=S\)
则 \(S \times a = a,2a,3a,\cdots,(p-1)a\)
\(\therefore (S\times a\bmod p=(S\bmod p\times\ a\bmod p)\bmod p\) \(({\rm gcd}(a,p)=1)\)
\(\therefore\) 上式\(=S\times (a\bmod p)\) 而 \({\rm gcd}(p,a\bmod p)=1\)
\(\therefore \prod_{i=1}^{p-1}i\equiv \prod_{i=1}^{p-1}a\times i\ (\bmod p)\)
\(\therefore (p-1)!\equiv a^{p-1}\times (p-1)!(\bmod p)\)
\(\therefore 1\equiv a^{p-1}(\bmod p)\)\(\therefore a\equiv a^p(\bmod p)\)
求逆元:
若 \(p\) 为质数,且 \(\gcd(a,p)=1\) ,则 \(a^{-1}\equiv a^{p-2}(\bmod p)\) 。 计算时间复杂度 \(O(\log p)\) 。
\(a^{p-1}\equiv 1(\bmod p)\)
\(\therefore a\times a^{p-2}\equiv 1(\bmod p)\)
\(\therefore a^{-1}\equiv a^{p-2}(\bmod p)\)
线性求逆元:
代码
inv[1] = 1;
for(int i=2; i<=n; ++i)
inv[i] = ((-1LL*(p/i) % p ) * inv[p%i] % p + p ) % p;
$ \because p\bmod i=p-\lfloor\frac{p}{i}\rfloor\times i$
\(\therefore p=\lfloor\frac{p}{i}\rfloor\times i+(p\bmod i)\)
\(\therefore 0\equiv\lfloor\frac{p}{i}\rfloor\times i+(p\bmod i)(\bmod p)\)
\(\therefore -(p\bmod i)\equiv\frac{p}{i}\times i(\bmod p)\)
\(\therefore -\frac{\lfloor \frac{p}{i}\rfloor\times i}{p\ \bmod\ i}\equiv 1(\bmod p)\)
\(\therefore \frac{1}{i}\equiv-\frac{\lfloor \frac{p}{i}\rfloor}{p\ \bmod\ i}\)
求阶乘逆元:
代码
inv[max1]=ksm(jie[max1],mod-2);//根据费马小定理求逆元
for(int i=max1-1;i>=1;i--){//逆元递推
inv[i]=cheng(inv[i+1],i+1);
}
对于已知的 \((n+1)!\) 的逆元,只需将其乘上 \(n+1\) 即可得到 \(n!\) 的逆元。即 \(\frac{1}{(n+1)!}\times (n+1)=\frac{1}{n!}\)
欧拉定理:
若正整数 \(a,n\) 互质,则 \(a^{\varphi(p)}\equiv1(\bmod p)\)
推论(扩展欧拉定理):
证明: \(a^b\equiv a^{b\ \bmod\ \varphi(p)}\ \ \gcd(a,p)=1\)
\[设 b=q\times \varphi(p)+r\\a^b\equiv a^{q\times\varphi(p)+r}\equiv (a^{\varphi(p)})^q\times a^r\equiv 1^q\times a^r\equiv a^r\equiv a^{b\ \bmod\ \varphi(p)} \]
若正整数 \(a,n\) 互质,则满足 \(a^x\equiv 1(\bmod n)\) 的最小正整数 \(x_0\) 是 \(\varphi(n)\) 的约数。
假设满足 \(a^x\equiv 1(\bmod n)\) 的最小正整数 \(x_0\) 不是 \(\varphi(n)\) 的约数。
设 \(\varphi(n)=qx_0+r(0<r<x_0)\)
\(\therefore a^{x_0}\equiv 1(\bmod n)\)
\(\therefore a^{qx_0}\equiv 1(\bmod n)\)
\(\because a^{\varphi(n)}\equiv 1(\bmod n)\)
\(\therefore a^r\equiv 1(\bmod n)\)
而 \(r<x_0\) ,这与 \(x_0\) 最小矛盾,所以假设不成立。
例: 求 \(a^b\bmod m\) ?
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e7+10;
int a, m;
char b[maxn];
int phi(int x) {
int res = x;
int cnt = sqrt(x);
for(int i=2; i<=cnt; ++i) {
if(x % i == 0) {
res = res / i * (i-1);
while(x % i == 0) x /= i;
}
}
if(x > 1) res = res / x * (x-1);
return res;
}
LL quick_pow(LL x, LL y) {
LL res = 1;
while(y) {
if(y&1)
res = (res*x)% m;
x = (x*x) % m;
y >>= 1;
}
return res % m;
}
int main () {
scanf("%d%d%s", &a, &m, b);
int Phi = phi(m); // m 的欧拉函数值
int rmd = 0;
int len = strlen(b);
int flag = 1;
for(int i=0; i<len; ++i) { // 将 b 对 Phi 取模
rmd = rmd * 10;
if(rmd >= Phi) flag = 0;
rmd = rmd % Phi;
rmd = rmd + (b[i]-'0');
if(rmd >= Phi) flag = 0;
rmd = rmd % Phi;
}
if(flag) // 当 b < Phi
printf("%lld\n", quick_pow(a, rmd));
else printf("%lld\n", quick_pow(a, rmd+Phi));
return 0;
}
相逢是问候:
这是一道非常简单且有意思的题:有两种操作: \(1\). \(\forall i\in[l,r],a_i=c^{a_i}\)\(2\). 求\(\sum_{i=1}^na_i\)
对于此题的修改操作,即是求 \(c\) ^ \(c\) ^ \(\cdots\) ^ \(a_i\) \((\bmod p)\) (此处“^”表示乘方)可以证明当层数足够多时其结果为常数。所以可以使用势能线段树进行修改,对于每个点的修改利用扩展欧拉定理求解,并统计其修改次数,当其足够多时便不用修改。但是此时 \(3\log\) 的复杂度还不足以通过此题,可以预处理快速幂从而省去一个 \(\log\) 的复杂度即可。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
ll s=0,k=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-')k=-k;
c=getchar();
}
while(c>='0'&&c<='9'){
s=(s<<3)+(s<<1)+(c^48);
c=getchar();
}
return s*k;
}
const int N=1e5+10,M=55;
int n,m,cnt;
ll p,c,a[N],phi[M],op,x,y,Mul1[N][M],Mul2[N][M];
bool fl,f1[N][M],f2[N][M];
struct node{
ll v,mn;
}t[N<<1];
inline ll Phi(ll V){
ll res=V,nq=sqrt(V);
for(register ll i=2;i<=nq;i++){
if(V%i==0){
res=res/i*(i-1);
while(V%i==0) V/=i;
}
}
if(V>1) res=res/V*(V-1);
return res;
}
inline void init(){
ll tmp=p; phi[0]=p;
while(tmp!=1){
tmp=Phi(tmp);
phi[++cnt]=tmp;
}
phi[++cnt]=1;
for(register int i=0;i<=cnt;i++){ //预处理快速幂
Mul1[0][i]=1;Mul2[0][i]=1;
for(register int j=1;j<=10000;j++){
Mul1[j][i]=Mul1[j-1][i]*c;
if(Mul1[j][i]>=phi[i])
Mul1[j][i]%=phi[i],f1[j][i]=1;
f1[j][i]|=f1[j-1][i];
}
f2[1][i]=f1[10000][i];
for(register int j=1;j<=10000;++j){
Mul2[j][i]=Mul2[j-1][i]*Mul1[10000][i];
if(Mul2[j][i]>=phi[i])
Mul2[j][i]%=phi[i],f2[j][i]=1;
f2[j][i]|=f2[j-1][i];
}
}
}
inline ll ksm(ll t,ll Mod){
ll mod=phi[Mod];
ll res,v1=t%10000,v2=t/10000;
fl=0;
res=Mul1[v1][Mod]*Mul2[v2][Mod];
if(res>=mod) res%=mod,fl=1;
else fl=fl|f1[v1][Mod]|f2[v2][Mod];
return res;
}
inline ll dfs(ll V,int deep,int pH){ // 运用扩展欧拉定理求答案 c^c^c...^c^ai
if(deep==pH){
if(V>=phi[deep]) fl=1,V%=phi[deep];
return V;
}
ll B=dfs(V,deep+1,pH);
if(fl) return ksm(B+phi[deep+1],deep);
return ksm(B,deep);
}
inline void pushup(int s){ // 势能线段树(执行足够多次操作后将会是一个固定值)
t[s].v=(t[s<<1].v+t[s<<1|1].v);
if(t[s].v>=p) t[s].v-=p;
t[s].mn=min(t[s<<1].mn,t[s<<1|1].mn);
}
inline void build(int s,int l,int r){
if(l==r){
t[s].v=a[l];
return;
}
int mid=(l+r)>>1;
build(s<<1,l,mid);
build(s<<1|1,mid+1,r);
pushup(s);
}
inline void update(int L,int R,int l,int r,int s){
if(t[s].mn>=cnt) return ;
if(L==R){
++t[s].mn;
fl=0;
t[s].v=dfs(a[L],0,t[s].mn);
return ;
}
int mid=(L+R)>>1;
if((l<=mid)&&(t[s<<1].mn<cnt)) update(L,mid,l,r,s<<1);
if((r>mid)&&(t[s<<1|1].mn<cnt)) update(mid+1,R,l,r,s<<1|1);
pushup(s);
}
inline ll query(int L,int R,int l,int r,int s){
if((l<=L)&&(R<=r)) return t[s].v;
int mid=(L+R)>>1;
ll res=0;
if(l<=mid) res+=query(L,mid,l,r,s<<1);
if(res>=p) res-=p;
if(r>mid) res+=query(mid+1,R,l,r,s<<1|1);
if(res>=p) res-=p;
return res;
}
int main() {
n=read();m=read();p=read();c=read();
for(register int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
init();
while(m--){
int op,x,y;
op=read();x=read();y=read();
if(op==0) update(1,n,x,y,1);
else printf("%lld\n",query(1,n,x,y,1));
}
return 0;
}
exgcd:
\(Bézout\): 对于任意整数 \(a,b\) 存在一堆整数 \(x,y\) 满足 \(ax+by=\gcd(a,b)\) 。
在 \(\gcd\) 最后当 \(b=0\) 时,显然 \(x=1,y=0\) 满足 \(a\times1+0\times 0=\gcd(a,0)\) 。
若 \(b>0\) ,则 \(\gcd(a,b)=\gcd(b,a\ {\rm mod}\ b)\) 假设有 \(x,y\) 满足 \(bx+(a\bmod b)y=\gcd(b,a\bmod b)\)
\(bx+(a\bmod b)y=bx+(a-b\lfloor\frac{a}{b}\rfloor)y=ay+b(x-\lfloor\frac{a}{b}\rfloor y)\)
\(\therefore x'=y,y'=x-\lfloor\frac{a}{b}\rfloor y\) 可得 \(ax'+by'=\gcd(a,b)\) 成立。
$\therefore $ 其通解可表示为 \(x=x_0+kb,y=y_0-ka\) 。
模板:
代码
int exgcd(int a, int b, int &x, int &y) {
if(b == 0) {
x = 1;
y = 0;
return a;
}
int d = exgcd(b, a%b, x, y);
int z = x;
x = y;
y = z - (a/b)*y;
return d; // 返回值是 gcd(a, b);
}
对于一般的方程 \(ax+by=c\) ,当且仅当 \(d\mid c\ (d=\gcd(a,b)\ )\) 时有解。
可以先求出 \(ax+by=d\) 的解 \(x_0,y_0\) 易得 \(ax+by=c\) 的解为 \((c/d)x_0,(c/d)y_0\) 。
$\therefore $ 其通解可表示为 \(x=\frac{c}{d}x_0+k\frac{b}{d},y=\frac{c}{d}y_0-k\frac{a}{d}\)
求逆元: 即 \(ax\equiv 1(\bmod p)\) 求 \(x\)
\(\because ax\equiv 1(\bmod p),\gcd(a,p)=1\)
\(\therefore ax=1+py\)
\(\therefore ax+p(-y)=1\)
使用 \(exgcd\) 求得 \(x\) 即可。
中国剩余定理:
设 \(m_1,m_2,\cdots,m_n\) 是两两互质的整数, \(M=\prod_{i=1}^nm_i,M_i=M/m_i,t_i\) 是线性同余方程 \(M_it_i\equiv 1(\bmod m_i)\) 的解,对于任意 \(n\) 个整数 \(a_1,a_2,\cdots,a_n\) 方程组
的解为 \(x=\sum_{i=1}^na_iM_it_i\)
\(\because M_i=M/m_i\)
\(\therefore M_i\) 是除 \(m_i\) 之外所有模数的倍数
\(\therefore \forall k!=i,a_iM_it_i\equiv0\ (\bmod m_k)\)
\(\because a_iM_it_i\equiv a_i\ (\bmod m_i)\)
\(\therefore x=\sum_{i=1}^na_iM_it_i\) 可使其成立
模板:
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 12;
typedef long long LL;
LL a[maxn], m[maxn];
void exgcd(LL a1, LL b, LL& x, LL& y)
{
if(b){
exgcd(b, a1%b, y, x);
y -= (a1/b)*x;
return ;
}
x = 1;
y = 0;
return ;
}
LL CRT(LL a[], LL m[], int n)
{
LL M=1, ans=0, Mi, x, y;
for(int i=1; i<=n; ++i)
M *= m[i];
for(int i=1; i<=n; ++i)
{
Mi = M/m[i];
exgcd(Mi, m[i], x, y); // 求出 Mi 在 模 mi意义下的乘法逆元
x = (x%m[i] + m[i]) % m[i];
ans = (ans + a[i]*x*Mi) % M;
}
return (ans+M) % M; // 求出最小非负整数解
}
int main()
{
int n;
scanf("%d", &n);
for(int i=1; i<=n; ++i)
scanf("%lld%lld", &m[i], &a[i]); // mi是模数, ai是是在模 mi 意义下的同余数
LL ans = CRT(a, m, n);
printf("%lld\n", ans);
}
扩展中国剩余定理:
与中国剩余定理同样,但 \(m_1,m_2,\cdots,m_n\) 不互质。
若 \(n=2\) :
\[\begin{cases}x\equiv a_1(\bmod m_1)\\x\equiv a_2(\bmod m_2)\end{cases}\Rightarrow \begin{cases}x= k_1m_1+a_1\\x= k_2m_2+a_2\end{cases}(k\in \mathbb{N}) \]\(\therefore m_1k_1+a_1=m_2k_2+a_2\ \ \Rightarrow \ \ m_1k_1-m_2k_2=a_2-a_1\)
当且仅当 \(\gcd(m_1,m_2)\mid a_2-a_1\) 时有解,用 \(exgcd\) 求得一组解 \((k_1',k_2')\) ,带入方程组中得 \(x=x_0\) 。
$\therefore $ \(x\) 的通解为 \(x=x_0+z\times {\rm lcm}(m_1,m_2)\)
令 \(M={\rm lcm}(m_1,m_2),A=x_0\) ,则 \(x=A+z\times M\Rightarrow x\equiv A(\bmod M)\)
这样就把两个同余式换成了一个同余式,以此类推即可求解。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;
LL r[maxn], m[maxn];
LL exgcd(LL a, LL b, LL& x, LL& y)
{
if(b){
LL d = exgcd(b, a%b, y, x);
y -= a/b*x;
return d;
}
x = 1; y = 0;
return a;
}
LL quick_mul(LL a, LL b, LL mod)
{
LL ans = 0;
while(b>0){
if(b&1)
ans = (ans + a) % mod;
a = (a << 1) % mod;
b >>= 1;
}
return ans;
}
LL excrt(int n)
{
LL M=m[1], R=r[1], k1, k2;
for(int i=2; i<=n; ++i)
{
LL a=M, b=m[i];
LL c = ((r[i]-R)%b + b) % b;// 求ax+by=c即求ax同余c(mod b),所以mod b对于答案没有影响
LL d = exgcd(a, b, k1, k2);
if(c%d != 0) return -1; // 无解
k1 = quick_mul(k1, c/d, b/d); // 找出方程 ak1+bk2=c的最小非负整数解
R += k1 * M;
M = a/d*b;
R = (R%M + M) % M;
}
return (R%M + M) % M;
}
int main()
{
int n;
scanf("%d", &n);
for(int i=1; i<=n; ++i)
scanf("%lld%lld", &m[i], &r[i]); // m[i] 表示模数, r[i] 表示余数
printf("%lld\n", excrt(n));
return 0;
}
莫比乌斯反演:
莫比乌斯函数:
设正整数 \(N\) 分解质因数为 \(N=p_1^{c_1}\times p_2^{c_2}\times \cdots\times p_m^{c_m}\) ,定义函数
注意莫比乌斯函数是积性函数。
同时其满足 \(\sum_{d|n} \mu(d)=[n=1]\)。
若只求一项莫比乌斯函数,分解质因数即可。
代码
int mobius(){
for(int i=2;i<=n;i++){
while(n%i==0){
c[++cnt]=i;
if(c[cnt]==c[cnt-1]) return 0;
n/=i;
}
}
if(n>1) cnt++;
if(cnt&1) return -1;
else return 1;
}
若求 \(1\sim N\) 的每一项数值,可以筛法计算。
埃氏筛
void mobius(){
for(int i=1;i<=n;i++) miu[i]=1,v[i]=0;
for(int i=2;i<=n;i++){
if(v[i]) continue;
miu[i]=-1;
for(int j=2*i;j<=n;j+=i){
v[j]=1;
if((j/i)%i==0) miu[j]=0;
else miu[j]*=i;
}
}
}
线性筛
void mobius(){
ipr[1]=miu[1]=1;
for(int i=2;i<=n;i++){
if(!ipr[i]) pri[++cnt]=i,miu[i]=-1;
for(int j=1;j<=cnt&&i*pr[j]<=n;j++){
ipr[i*pr[j]]=1;
if(i%pr[j]==0){
miu[i*pr[j]]=0;
break;
}
miu[i*pr[j]]=-miu[i];
}
}
}
[POI2007]ZAP-Queries
给出 \(a,b,d\),求满足 \(1 \leq x \leq a\),\(1 \leq y \leq b\),且 \(\gcd(x,y)=d\) 的二元组 \((x,y)\) 的数量。
题目即求:
\[\sum_{i=1}^a\sum_{j=1}^b [\gcd(i,j)==k]\\=\sum_{i=1}^{\lfloor\frac{a}{k}\rfloor}\sum_{i=1}^{\lfloor\frac{b}{k}\rfloor} [\gcd(i,j)==1] \]设 \(D[i,j,k]\) 表示满足 \(1\leq x\leq a,1\leq y\leq b\) 且 \(k\mid \gcd(x,y)\) 的二元组的方案数。显然为 \(k\mid a\ \&\& \ k\mid b\) 的方案数。\(1\sim a\) 中 \(k\) 的倍数有 \(\lfloor\frac{a}{k}\rfloor\) 个,所以 \(D[a,b,k]=\lfloor\frac{a}{k}\rfloor\times\lfloor\frac{b}{k}\rfloor\)设 \(F[a,b]\) 表示满足 \(1\leq x\leq a,1\leq y\leq b\) 且 \(\gcd(x,y)=1\) 的方案数,会发现 \(D[a,b,i]\) 的系数恰好就是 \(\mu(i)\) ,即:
\[F[a,b]=\sum_{i=1}^{\min(a,b)} \mu(i)\times D[a,b,i] \]然后发现 \(\forall i\in[x,\min(\lfloor\frac{a}{\lfloor\frac{a}{x}\rfloor}\rfloor,\lfloor\frac{b}{\lfloor\frac{b}{x}\rfloor}\rfloor)],D[a,b,i]\) 都相等,就可以用整除分块维护答案,共有 \(O(\sqrt{a}+\sqrt{b})\) 个段。并预处理 \(\mu(i)\) 的前缀和,即可快速求得答案。
狄利克雷卷积:
定义卷积 \(f*g\) 为 \(h=f*g\to h(x)=\sum_{d|n}f(d)g(\frac{n}{d})\)
运算符合交换律,结合律,分配率。同时区别于其他卷积的他符合若 \(f,g\) 是积性函数则 \(h=f*g\) 是积性函数。
有单位元 \(\varepsilon(n)=[n=1]\) 满足 \(f*\varepsilon =f\)。
常见函数和等式有 \(I(x)=1,id(x)=x\)。有 \(\varphi *I=id,\mu *I=\varepsilon\)。
根据 \(\mu *I=\varepsilon\) 有 \(f*I=g\to f=g*\mu\),这就是莫比乌斯反演的核心。
额外的,还有 \(\varphi=id*\mu\)。
首先有 \(\varphi *I=id\),两边同卷 \(\mu\),有 \(\varphi *(I*\mu)=\varphi *\varepsilon=\varphi=id*\mu\)
P5518 [MtOI2019] 幽灵乐团 / 莫比乌斯反演基础练习题
狄利克雷逆:
根据单位元,同理可以定义 \(f\) 的狄利克雷逆 \(g=f^{-1}\) 满足 \(f*g=\varepsilon\)。
特别的,若 \(f\) 是积性函数则 \(f^{-1}\) 也是积性函数。
- 当 \(n=1\) 时,有 \(g(1)f(1)=\varepsilon (1)=1\)。所以有条件 \(f\) 存在狄利克雷逆的充分条件是 \(f(1)\not= 0\)。
- 考虑 \(n>1\) 的 \(g(n)\) 怎么求,有 \(0=\sum_{d|n}g(d)f(\frac{n}{d})\),我们要求 \(g(n)\),所以可以把等式右边最后一项拆出来再化简,就有:
至此得到了 \(g(n)\) 的递推式,可以做到调和级数复杂度求解,同时可以发现只要分母 \(f(1)\not= 0\),\(g(n)\) 就一定有值。所以上述条件也是充要的。
狄利克雷前缀和:
对于一个数论函数 \(f\),求 \(s(n)=\sum_{d|n} f(d)\)。这个显然可以调和计数来做。
还可以考虑高位前缀和做法,枚举质数 \(p\),贡献 \(s(i)\to s(i\times p)\),根据 \(\sum_{p\in prime} \frac{1}{p}=O(\log\log)\),复杂度是 \(O(n\log\log)\)。
相应的可以做狄利克雷差分。
线性筛积性函数的通用形式:
计 \(n\) 的最小质因数 \(p\) 在 \(n\) 中含有的幂级数 \(p^c=low(n)\)。
根据线性筛的代码,令 \(n=i\times p_j\)
- 当 \(p_j\nmid i\) 时:\(f(n)=f(i)\times f(p_j)\)。
- 当 \(p_j\mid i\) 时:
- 若 \(n\) 只有一个质因数即 \(n=low(n)\),需要暴力算单点 \(f(n)\)。
- 否则可以将 \(n\) 中所有 \(p\) 的因子除掉,即变成 \(f(n)=f(i/low(i))\times f(low(n))\) 显然 \(low(n)\) 和 \(i/low(i)\) 互质。
\(low(n)\) 可以在线性筛时一起筛出来。
代码
for(int i = 2; i < N; i++) {
if(!vis[i]) pr[++cnt] = i, f[i] = ..., low[i] = i;
for(int j = 1; j <= cnt && i * pr[j] < N; j++) {
vis[i * pr[j]] = 1;
if(i % pr[j] == 0) { // 此时 i 与 pr 不互质, 需要另辟蹊径
low[i * pr[j]] = low[i] * pr[j];
if(i == low[i]) f[i * pr[j]] = ...; // 根据 f(p ^ k) 算 f(p ^ (k + 1))
else f[i * pr[j]] = f[i / low[i]] * f[low[i * pr[j]]]; break;
} f[i * pr[j]] = f[i] * f[pr[j]], low[i * pr[j]] = pr[j]
// i * pr 最小质因子次数 = 1, 可以通过 f[i] * f[pr] 算 f[i * pr] (积性函数的性质)
}
}
线性筛狄利克雷积性函数卷积:
由于 \(h=f*g\) 也是积性所以直接和上面一样做就行,当 \(n=low(n)\) 时,\(n\) 一定是某个质因子的 \(k\) 次幂,\(O(k)\) 暴力单点求值,若 \(f,g\) 可以 \(O(1)\) 得到点值,那么复杂度还是 \(O(n)\) 的。
积性卷积非积性:
对于一个积性函数 \(f\),其狄利克雷生成函数 \(\sum_i f(i)x^i\) 可以根据唯一分解定理拆成若干质数幂处生成函数的狄利克雷卷积值。
在 \(f\) 卷积非积性函数 \(g\) 时,可以暴力枚举 \(p,k\),把所有 \(i\leqslant\lfloor\frac{n}{p^k}\rfloor\) 贡献 \(f(p^k)g(i)\) 到 \(h(ip^k)\),对于其他的 \(i\) 乘积会超过 \(n\) 是没用的,然后令 \(h\) 为新的 \(g\) 再继续做。也就是用 \(g\) 依次卷积函数 \(\sum_kf_{p_{i}}(p^k)x^{p^k}\)。
复杂度是 \(\sum_p\sum_k\frac{n}{p^k}=O(n\log\log n)\)。