【luogu P4213】【模板】杜教筛(Sum)(数学)(整除分块)

【模板】杜教筛(Sum)

题目链接:luogu P4213

题目大意

要你求 φ 函数的前缀和和 μ 函数的前缀和。
(分别是欧拉函数和莫比乌斯函数)

思路

前置知识(们)

积性函数:对于两个互质的数 x,yf(xy)=f(x)f(y),那 f 就是积性函数。
完全积性函数:对于任意两个整数 x,yf(xy)=f(x)f(y),那 f 就是完全积性函数。

一些积性函数:φ,μ,d,σ
(分别是欧拉函数,莫比乌斯函数,约数个数,约数个数和)
一些完全积性函数:ϵ,I,id
ϵ(n)=[n=1],I(n)=1,id(n)=n


狄利克雷卷积:
有两个函数 f,g,它们的狄利克雷卷积:(fg)(n)=d|nf(d)g(nd)

不难看出,它满足交换律和结合律。
然后单位元就是 ϵ

然后又一些性质:(根据定义简单推一推都不难看出)
μI=ϵ
φI=id
μid=φ


莫反:

如果 g(n)=d|nf(d)
那么 f(n)=d|nμ(d)g(nd)

这个地方的证明其实可以用 μI=ϵ

给出条件相当于 g=fI

然后 gμ=fIμ=fϵ=f

杜教筛

杜教筛就是用来求积性函数的前缀和 sum(n)=i=1nf(i)

考虑再找一个积性函数 g,求他们的狄利克雷卷积:
i=1n(fg)(i)
=i=1nd|nf(d)g(id)
=d=1ng(d)i=1ndf(i)
=d=1ng(d)sum(nd)

然后考虑 g(1)sum(n),根据前缀和:
=i=1ng(i)sum(ni)i=2ng(i)sum(ni)
=i=1n(fg)(i)i=2ng(i)sum(ni)

那这个式子就是我们杜教筛要用的式子了。

有什么用呢,大概就是对于每个你要求的 f,如果你能找到一个合适的 g,它和 f 乘起来很好算而且 g 也好搞的话就可以拿来用。

就这题的例子,μI=ϵ,所以左边部分就 ϵ 的前缀和是 1,然后右边你可以用整除分块 O(n) 搞。
然后 φI=id,然后左边部分就 id 的前缀和是 (1+n)n2,右边也是整除分块过去。
(然后这两个右边 g(i) 因为是整除分块也可以同前缀和求出,因为是 I 所以就是 rl+1,即块的大小)

然后你可以预处理出 n23 以内的结果,复杂度就可以压到 O(n23)
(然后你还可以用 map 弄个小小的记忆化)

代码

#include<map> #include<cstdio> #define ll long long using namespace std; //const int Maxn = 1664511; const int Maxn = 2000000; int T, np[Maxn + 1], prime[Maxn + 1]; int miu[Maxn + 1], x; ll phi[Maxn + 1]; map <int, int> ans_miu; map <int, ll> ans_phi; void init() {//预处理 n^{2/3} 的部分 miu[1] = phi[1] = 1; for (int i = 2; i <= Maxn; i++) { if (!np[i]) { prime[++prime[0]] = i; miu[i] = -1; phi[i] = i - 1; } for (int j = 1; j <= prime[0] && i * prime[j] <= Maxn; j++) { if (i % prime[j]) miu[i * prime[j]] = -miu[i], phi[i * prime[j]] = phi[i] * (prime[j] - 1), np[i * prime[j]] = prime[j]; else { miu[i * prime[j]] = 0, phi[i * prime[j]] = phi[i] * prime[j], np[i * prime[j]] = prime[j]; break; } } } for (int i = 1; i <= Maxn; i++)//前缀和起来 miu[i] += miu[i - 1], phi[i] += phi[i - 1]; } ll get_phi(int x) { if (x <= Maxn) return phi[x]; if (ans_phi[x]) return ans_phi[x]; ll re = 1ll * (1ll + x) * x / 2;//id 函数的前缀和 for (ll l = 2, r; l <= x; l = r + 1) { r = x / (x / l); re -= 1ll * (r - l + 1) * get_phi(x / l); } return ans_phi[x] = re; } int get_miu(int x) { if (x <= Maxn) return miu[x]; if (ans_miu[x]) return ans_miu[x]; ll re = 1;//ϵ 函数的前缀和 for (ll l = 2, r; l <= x; l = r + 1) { r = x / (x / l); re -= 1ll * (r - l + 1) * get_miu(x / l); } return ans_miu[x] = re; } int main() { init(); scanf("%d", &T); while (T--) { scanf("%d", &x); printf("%llu %d\n", get_phi(x), get_miu(x)); } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P4213.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(35)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示