杜教筛学习笔记
杜教筛学习笔记
杜教筛可以在非线性时间 \(O(n^{\frac 23})\) 内快速求出积性函数的前缀和
前置知识
积性函数
积性函数:对 \(\forall a,b\in Z\and gcd(a,b)==1\) 有 \(f(a\cdot b)=f(a)\cdot f(b)\) 的数论函数 \(f\).
完全积性函数:对 \(\forall a,b\in Z\) 有 \(f(a\cdot b)=f(a)\cdot f(b)\) 的数论函数 \(f\).
积性函数举例:\(\varphi,\mu,d,\sigma,\sigma^k\)
完全积性函数举例:\(1,id,id^k,\varepsilon\)
Dirichlet 卷积
数论函数 \(f\) 和 \(g\) 的 \(Dirichlet\) 卷积为:
数论分块
对于含有 \(\lfloor\frac{n}{i}\rfloor(n为常数)\) 的求和式,观察式子的性质
\(\lfloor\frac{n}{i}\rfloor\) 为非严格单调递减数列,且存在多个数值相等的区间 \([l,r]\)
满足 \(\lfloor\frac{n}{i}\rfloor=\lfloor\frac{n}{j}\rfloor,(i,j\in[l,r])\)
对任意 \(i\),可求得其对应的区间右端点 \(r=\left\lfloor{\dfrac{n}{\left\lfloor{\dfrac{n}{i}}\right\rfloor}}\right\rfloor\)
在求和时可以考虑同时求出数值相等区间的总和
采用双指针的思想,设变量 \(l,r\) 为此区间的左右端点
即可通过计算得出,每个区间对应的 \(l,r\) 值
以便于进行统计答案
复杂度分析
给定 \(n\),求解 \(\displaystyle \sum_{i=1}^{n}\lfloor\frac{n}{i}\rfloor,n\in N\)
- \(i\leq\lfloor\sqrt{n}\rfloor\) 时,\(\lfloor\frac{n}{i}\rfloor\) 有最多 \(\lfloor\sqrt{n}\rfloor\) 种取值
- \(i\leq\lfloor\sqrt{n}\rfloor\) 时,\(\lfloor\frac{n}{i}\rfloor\leq \lfloor\sqrt{n}\rfloor\),\(\lfloor\frac{n}{i}\rfloor\) 最多有 \(\lfloor\sqrt{n}\rfloor\) 种取值
复杂度 \(O(\sqrt{n})\).
int calc(int n) {
int res = 0;
for(int l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
res += (n / l) * (r - l + 1);
}
return res;
}
方法
假设当前需求解 \(f\) 的前缀和 \(S\),即求解
若直接求解 \(S\) 的复杂度比较高,考虑对 \(f\) 进行卷积运算后 进行前缀和运算
移项,系数化 \(1\) 可得
观察式子可得,求解 \(S(n)\) 需保证可以快速求出 \(f*g\) 的前缀和
而分子上 后一部分可以通过 数论分块 来优化解决
应用
莫比乌斯函数前缀和
令 \(f=\mu,g=1\),由 \(Dirichlet\) 卷积可得 \(f*g=\mu*1=\varepsilon\)
由数论分块可得,\(\lfloor\frac{n}{i}\rfloor\) 的取值有 \(O(\sqrt{n})\) 个,时间复杂度为 \(O(\sum\limits_{i=1}2^{\frac{1}{2^i}}) \approx O(n^{\frac{3}{4}})\).
考虑优化时间复杂度,对于数据较小的部分,直接 \(O(n)\) 线性筛筛出
数据较大的部分则由杜教筛筛出,并采用 记忆化 优化时间复杂度
设线性筛预处理出 \(k\) 个数,余下的 \(n-k\) 个则由杜教筛筛出
时间复杂度为 \(O(k+(n-k)^{\frac{3}{4}})\)
当 \(k=(n-k)^{\frac{3}{4}}\) 时,时间复杂度取最小值,约为 \(O(n^{\frac{2}{3}})\).
欧拉函数前缀和
令 \(f=\varphi,g=1\),由 \(Dirichlet\) 卷积可得 \(f*g=\varphi*1=id\)
同之前的方法,对于小数据线性筛预处理,大数据杜教筛记忆化
时间复杂度 \(O(n^{\frac{2}{3}})\).
#include <iostream>
#include <cstdio>
#include <map>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int N = 6e6 + 5;
typedef long long LL;
int read() {
int x = 0, f = 1; char ch;
while(! isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 3) + (x << 1) + (ch ^ 48));
return x * f;
}
template <class T> T Max(T a, T b) { return a > b ? a : b; }
template <class T> T Min(T a, T b) { return a < b ? a : b; }
bool vis[N];
int n, cnt, pri[N], sphi[N], smu[N];
map <int, int> mpmu, mpphi;
inline void calc(int x) {
smu[1] = sphi[1] = 1;
for(int i = 2; i <= x; ++ i) {
if(vis[i] == 0) pri[++ cnt] = i, smu[i] = -1, sphi[i] = i - 1;
for(int j = 1; j <= cnt && i * pri[j] <= x; ++ j) {
vis[i * pri[j]] = 1;
if(i % pri[j] == 0) {
sphi[i * pri[j]] = sphi[i] * pri[j];
break;
}
else smu[i * pri[j]] = - smu[i], sphi[i * pri[j]] = sphi[i] * (pri[j] - 1);
}
}
for(int i = 1; i <= 6e6; ++ i) smu[i] += smu[i - 1], sphi[i] += sphi[i - 1];
}
int sumphi(int n) {
if(n <= 6e6) return sphi[n];
if(mpphi[n]) return mpphi[n];
int res = n * (n + 1) / 2;
for(int i = 2, j; i <= n; i = j + 1) {
j = n / (n / i);
res -= sumphi(n / i) * (j - i + 1);
}
return mpphi[n] = res;
}
int summu(int n) {
if(n <= 6e6) return smu[n];
if(mpmu[n]) return mpmu[n];
int res = 1;
for(LL i = 2, j; i <= n; i = j + 1) {
j = n / (n / i);
res -= summu(n / i) * (j - i + 1);
}
return mpmu[n] = res;
}
void solve() {
scanf("%lld", &n);
printf("%lld %lld\n", sumphi(n), summu(n));
}
signed main() {
int T = read(); calc(6e6);
while(T --> 0) solve();
return 0;
}
莫比乌斯函数的平方值前缀和
令 \(f=\mu^2,g=1\),由 \(Dirichlet\) 卷积可得 \(f*g=\mu^2*1=\mu\)
在求 \(S(n)\) 过程中,要处理出莫比乌斯函数 \(\mu\) 的前缀和,同样采用杜教筛
时间复杂度 \(O(n^{\frac{2}{3}})\).
莫比乌斯函数值的平方前缀和
\(f(i)=\mu(i)^2\),寻找合适的 \(g(i)\) 使得 \(f*g\) 的前缀和方便被求出
令 \(g(x)=[x=k^2],\ k\in N^+\),则有 \(f*g=1\)
证明:
-
\(d_2\) 为完全平方数:只有当 \(d_2\) 为 \(n\) 约数中最大的完全平方数时
\(f(d_1)\cdot g(d_2)\) 才为 \(1\),否则 \(\mu(d_1)=0\)
-
\(d_2\) 不是完全平方数:\(g(d_2)=0\)
\(\therefore\ f*g=1\)
时间复杂度:\(O(n^{\frac{2}{3}})\).

浙公网安备 33010602011771号