首页 > 其他分享 >杜教筛

杜教筛

时间:2024-12-01 22:10:57浏览次数:4  
标签:lfloor limits dfrac sum rfloor 杜教 mu

1 积性函数和狄利克雷卷积

1.1 积性函数

1.1.1 定义

积性函数在以前的学习中遇到过很多,例如莫比乌斯函数 \(\mu(n)\),欧拉函数 \(\varphi(n)\) 等等。那么我们对积性函数定义如下:

  • 称定义域在正整数上的函数叫做数论函数(也叫算数函数)。
  • 对于一个数论函数 \(f(n)\),如果 \(f(xy)=f(x)f(y)\) 对于任意互质的 \(x,y\in\mathbb{N}_+\) 都成立,则称 \(f(n)\) 为积性函数。
  • 特别的,对于一个数论函数 \(f(n)\),如果 \(f(xy)=f(x)f(y)\) 对于任意的 \(x,y\in\mathbb{N}_+\) 都成立,则称 \(f(n)\) 为完全积性函数。

1.1.2 常见积性函数

  • 单位函数:\(\varepsilon(n)=[n=1]\)(完全积性)。
  • 恒等函数:\(\text{id}_k(n)=n^k\)(完全积性)。其中 \(\text{id}_1(n)=n\),通常简记为 \(\text{id}(n)\)。
  • 常数函数:\(1(n)=1\)(完全积性)。
  • 除数函数:\(\sigma_k(n)=\sum\limits_{d\mid n} d^k\)。其中 \(\sigma_0(n)\) 即因子个数,通常简记为 \(d(n)\);\(\sigma_1(n)\) 即因子之和,通常简记为 \(\sigma(n)\)。
  • 欧拉函数:\(\varphi(n)=\sum\limits_{i=1}^n[\gcd(i,n)=1]\)。
  • 莫比乌斯函数:\(\mu(n)\)。

1.2 狄利克雷卷积

1.2.1 定义

对于两个数论函数 \(f(x)\) 与 \(g(x)\),它们的狄利克雷卷积得到的结果 \(h(x)\) 定义为:

\[h(x)=\sum_{d\mid x} f(d)g(\dfrac xd) \]

上式通常简记为:

\[h=f*g \]

例如在 \(1.1.2\) 中出现的一些函数,我们会有如下式子:

  • \(\varepsilon=\mu*1\)。
  • \(d=1*1\)。
  • \(\sigma=\text{id}*1\)。
  • \(\varphi=\mu*\text{id}\)。

1.2.2 性质

  • 交换律:\(f*g=g*f\)。
  • 结合律:\((f*g)*h=f*(g*h)\)。
  • 分配率:\((f+g)*h=f*h+g*h\)。

2 杜教筛

2.1 引入

首先需要了解的前置知识:积性函数、狄利克雷卷积、数论分块。

杜教筛处理的是对于一类数论函数 \(f(n)\),求其前缀和 \(S(n)=\sum\limits_{i=1}^nf(i)\) 的值。通常情况下 \(n\) 会很大,所以线性复杂度是不可行的。我们考虑数论分块,假如我们能够构造一个 \(S(n)\) 关于 \(S(\lfloor\dfrac ni\rfloor)\) 的递推式,就可以用数论分块快速求解了。

2.2 算法思想

对于一个数论函数 \(g\),会有如下等式:

\[\begin{aligned} \sum_{i=1}^n (f*g)(i)=&\sum_{i=1}^n\sum_{d\mid i}g(d)f(\dfrac i d)\\ =&\sum_{i=1}^n\sum_{j=1}^{\lfloor\frac ni\rfloor}g(i)f(j)\\ =&\sum_{i=1}^ng(i)\sum_{j=1}^{\lfloor\frac ni\rfloor}f(j)\\ =&\sum_{i=1}^ng(i)S(\lfloor\dfrac ni\rfloor) \end{aligned} \]

于是会有如下递推式:

\[\begin{aligned} g(1)S(n)=&\sum_{i=1}^n g(i)S(\lfloor\dfrac ni\rfloor)-\sum_{i=2}^n g(i)S(\lfloor\dfrac ni\rfloor)\\ =&\sum\limits_{i=1}^n(f*g)(i)-\sum_{i=2}^n g(i)S(\lfloor\dfrac ni\rfloor) \end{aligned} \]

假如我们可以构造适当的函数 \(g\),使得:

  • 可以快速求解出 \(\sum\limits_{i=1}^n (f*g)(i)\)。
  • 可以快速求解出 \(g(i)\) 的前缀和,以便后面使用数论分块求解 \(\sum\limits_{i=2}^n g(i)S(\lfloor\dfrac ni\rfloor)\)。

我们就能在较短时间内求得 \(g(1)S(n)\)。

具体来讲,杜教筛的时间复杂度是 \(O(n^{3/4})\) 的。如果我们可以快速求出前 \(n^{2/3}\) 项的函数值(例如使用线性筛),就可以降低复杂度至 \(O(n^{2/3})\)。值得注意的是,这个时间复杂度是基于记忆化搜索的,因此需要使用 map 等数据结构存储下 \(S(i)\) 的值。

2.3 实例

例 1:【模板】杜教筛

题意:求 \(\sum\limits_{i=1}^n\varphi(i)\) 和 \(\sum\limits_{i=1}^n \mu(i)\)。

先考虑求 \(S_1(n)=\sum\limits_{i=1}^n \mu(i)\)。

根据狄利克雷卷积可知,\(\varepsilon=\mu*1\),所以直接构造 \(g(n)=1(n)\) 即可,于是:

\[\begin{aligned} S_1(n)=&\sum\limits_{i=1}^n \mu(i)\\ =&\sum\limits_{i=1}^n \varepsilon(i)-\sum\limits_{i=2}^n1(i)S_1(\lfloor\dfrac ni\rfloor)\\ =&1-\sum\limits_{i=2}^nS_1(\lfloor\dfrac ni\rfloor) \end{aligned} \]

然后考虑求 \(S_2(n)=\sum\limits_{i=1}^n\varphi(i)\),实际上这个用莫比乌斯反演更容易得出,最后可以转化为求 \(\mu\) 的前缀和。不过我们同样可以使用杜教筛求解。

关于欧拉函数有一个广为人知的性质:\(\sum\limits_{d\mid n}\varphi(d)=n\),即 \(\varphi*1=\text{id}\)。通过这个也就可以证明出上文提到的 \(\varphi=\mu*\text{id}\) 的结论了。

我们仍然构造 \(g(n)=1(n)\),于是:

\[\begin{aligned} S_2(n)=&\sum\limits_{i=1}^n \varphi(i)\\ =&\sum\limits_{i=1}^n \text{id}(i)-\sum\limits_{i=2}^n1(i)S_2(\lfloor\dfrac ni\rfloor)\\ =&\dfrac{n(n+1)}2-\sum\limits_{i=2}^nS_2(\lfloor\dfrac ni\rfloor) \end{aligned} \]

代码如下:

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#define int long long

using namespace __gnu_pbds;
using namespace std;

const int Maxn = 2e6 + 5;
const int Inf = 2e9;

int T;
int n;

int prim[Maxn], cnt, mu[Maxn], phi[Maxn];
bool vis[Maxn];

const int N = 2e6;
void init() {
	mu[1] = 1;
	phi[1] = 1;
	for(int i = 2; i <= N; i++) {
		if(!vis[i]) {
			prim[++cnt] = i;
			mu[i] = -1;
			phi[i] = i - 1;
		}
		for(int j = 1, x; j <= cnt && (x = prim[j] * i) <= N; j++) {
			vis[x] = 1;
			if(i % prim[j] == 0) {
				mu[x] = 0;
				phi[x] = phi[i] * prim[j];
				break;
			}
			mu[x] = -mu[i];
			phi[x] = phi[i] * (prim[j] - 1);
		}
	}
	for(int i = 1; i <= N; i++) {
		mu[i] += mu[i - 1];
		phi[i] += phi[i - 1];
	}
}

gp_hash_table <int, int> smu, sphi;

int sum_mu(int x) {
	if(x <= N) return mu[x];
	if(smu[x]) return smu[x];
	int l = 2, r = 0, res = 1;
	while(l <= x) {
		r = x / (x / l);
		res -= sum_mu(x / l) * (r - l + 1);
		l = r + 1;
	}
	return smu[x] = res;
}

int sum_phi(int x) {
	if(x <= N) return phi[x];
	if(sphi[x]) return sphi[x];
	int l = 2, r = 0, res = (x + 1) * x / 2;
	while(l <= x) {
		r = x / (x / l);
		res -= sum_phi(x / l) * (r - l + 1);
		l = r + 1;
	}
	return sphi[x] = res;
}

void solve() {
	cin >> n;
	cout << sum_phi(n) << " " << sum_mu(n) << '\n';
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> T;
	init();
	while(T--) solve();
	return 0;
}

例 2:[CQOI2015] 选数

题意: 求出在值域区间 \([L,H]\) 中选出 \(n\) 个数且它们的 \(\gcd\) 恰为 \(k\) 的方案数。

这是经典 \(\gcd\) 容斥了,设 \(f(x)\) 表示选出的 \(n\) 个数的 \(\gcd\) 是 \(x\) 的倍数的方案数,\(g(x)\) 表示选出的 \(n\) 个数的 \(\gcd\) 恰为 \(x\) 的方案数。容易得到:

\[f(x)=\sum_{x\mid d}g(d) \]

根据莫比乌斯反演可得:

\[g(x)=\sum_{x\mid d}\mu(\dfrac dx)f(d) \]

根据定义,我们又知道 \(f(x)=(\lfloor\dfrac R x\rfloor-\lceil\dfrac Lx\rceil+1)^n=(\lfloor\dfrac R x\rfloor-\lfloor\dfrac {L-1}x\rfloor)^n\),所以我们就可以轻松得出:

\[g(x)=\sum_{x\mid d}\mu(\dfrac dx)(\lfloor\dfrac R d\rfloor-\lfloor\dfrac {L-1}d\rfloor)^n \]

但是发现这个式子无法通过数论分块去优化复杂度,因此需要另辟蹊径。考虑在原始的 \(g(x)\) 式子中不去枚举 \(d\),转而去枚举 \(\dfrac dx\),可以得到:

\[\begin{aligned} g(x)=&\sum_{i=1}^{\lfloor\frac Rk\rfloor}\mu(i)f(ki)\\ =&\sum_{i=1}^{\lfloor\frac Rk\rfloor}\mu(i)(\lfloor\dfrac R {ki}\rfloor-\lfloor\dfrac {L-1}{ki}\rfloor)^n \end{aligned} \]

令 \(R'=\lfloor\dfrac Rk\rfloor,L'=\lfloor\dfrac {L-1}{k}\rfloor\),则:

\[g(x)=\sum_{i=1}^{R'}\mu(i)(\lfloor\dfrac {R'} {i}\rfloor-\lfloor\dfrac {L'}{i}\rfloor)^n \]

此时整个式子就可以数论分块求解了,但是此时发现线性求 \(\mu\) 的前缀和复杂度仍然会炸,所以使用杜教筛求其前缀和即可。

标签:lfloor,limits,dfrac,sum,rfloor,杜教,mu
From: https://www.cnblogs.com/UKE-Automation/p/18580436

相关文章

  • [笔记]杜教筛 & Powerful Number 筛
    杜教筛杜教筛的作用杜教筛可以快速求出积性函数前缀和。如\(\varphi\),\(\mu\)等。什么是杜教筛定义\(f(x)\)为一个积性函数,求\(F(x)=\sum\limits_{i=1}^{n}f(x)\)。考虑构造函数\(h,g\),使得\(h=f*g\),即\(h(n)=\sum\limits_{d|n}f(d)g(\dfrac{n}{d}......
  • 杜教筛入门
    当学Min25的一个前置知识。算法内容。定义\(S(n)=\sum_{i=1}^nf(i)\)。对于一个函数\(g\),有:\[\begin{aligned}\sum_{i=1}^n(f\timesg)(i)&=\sum_{i=1}^n\sum_{d|i}f(\frac{i}{d})g(d)\\&=\sum_{d=1}^ng(d)\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}f(i)\\&am......
  • 算法学习笔记(23):杜教筛
    杜教筛参考来源:OI-Wiki,网上博客线性筛可以在线性时间求积性函数前缀和,而杜教筛可以用低于线性时间求解积性函数前缀和。我们考虑\(S(n)\)就是积性函数的前缀和,所以我们尝试构造关于\(\largeS(n)\)关于\(\largeS(\lfloor\frac{n}{i}\rfloor)\)的递推式。对于任意......
  • 杜教筛学习笔记
    杜教筛学习笔记杜教筛被用于求解某一数论函数\(f\)的前缀和,即对于形如\(S(n)=\sum_{i=1}^nf(i)\)形式的函数\(S\),杜教筛能够在小于线性复杂度的复杂度内求解。算法思想尝试构造一个函数\(S\)的递推式。选择一个数论函数\(g\),那么根据狄利克雷卷积可以得到:\[\begin{ali......
  • 小红不想做莫比乌斯反演杜教筛求因子和的前缀和(枚举)--牛客周赛 Round 39-E
    #include<bits/stdc++.h>usingnamespacestd;#defineintlonglong#defineendl'\n'#defineinf1e18constintmod=1e9+7;constintN=2e5+5;intn,m,p,x;voidsolve(){ cin>>n>>m>>p>>x; intans=0; for(inti=1;i&......
  • 杜教筛
    这是一种对于一个数论函数\(f(n)\),计算\(S(n)=\sum_{i=1}^nf(i)\)的快速方法。构造两个积性函数\(h,g\)满足\(h=g*f\),根据卷积的定义,有\(h(i)=\sum_{d|i}g(d)f(\frac{i}{d})\),对\(h\)求和,有:\[\sum_{i=1}^nh(i)=\sum_{i=1}^n\sum_{d|i}g(d)f(\frac{i}{d})\]\[=\sum_{......
  • (未完工)莫反与杜教筛
    \(p\)是质数。\(p^k\)是质数的幂。广告https://github.com/August-Light/NTFuncs这是一个关于各种数论函数的Python库!前置知识数论分块模板题1:[UVA11526]H(n)模板题2:[LuoguP2261][CQOI2007]余数求和线性筛&线性筛数论函数模板题1:[LuoguP3383]【模板......
  • 杜教筛——亚线性处理数论函数求和
    问题引入给定一个数论函数\(f(x)\),求\(\sum\limits_{i=1}^nf(i)\)。对\(n\le10^7\)甚至\(n\le10^8\)都是好做的,\(\mathcalO(n)\)解决即可,但如果\(n<2^{31}\)呢?这就需要亚线性时间复杂度的算法,杜教筛就是其一。杜教筛杜教筛是一种能在幂时间\(\mathcalO(......
  • 杜教筛学习笔记
    杜教筛是求一个数论函数f的前缀和,令其为S我们考虑构造一个数论函数g,根据狄利克雷卷积\[\begin{aligned}\sum_{i=1}^{n}(f*g)(i)&=\sum_{i=1}^{n}\sum_{d\midi}g(d)f\left(\frac{i}{d}\right)\\&=\sum_{i=1}^{n}g(i)S\le......
  • 杜教筛
    (抄袭Alex_Wei)一、知识点假设我们先现在希望求出函数\(f\)在\(n\)处的前缀和\(s(n)=\sum\limits_{i=1}^nf(i)\),我们构造另一个数论函数\(g\),设\(h=f*g\),则\[\begin{aligned}&\sum\limits_{i=1}^nh(i)\\=&\sum\limits_{ij\leqn}f(i)g(j)\\=&\sum\limits_{d=1}^ng......