杜教筛小结

这个算法十分的强... 一般就是用于卡一道数论推结论题最后的20~30分... 被迫来学习QwQ

算法讲解

这个一般用来筛一个积性函数在1010左右的前缀和...

为了了解一下,可以看看 2016年国家候选队论文中的任之洲积性函数求和的几种方法

讲讲一些套路吧...

首先有ymy大佬告诉我的一个神奇东西,这个似乎很有用...

fg=l (Dirichlet卷积,下同) 若GL易求(g,l的前缀和) 则F易求 (f的前缀和).

L(n)g(1)F(n)=i=2ng(i)F(ni). 证明的话可以参照后面的推导来证。

Dirichlet卷积 :

定义两个数论函数f(x),g(x)Dirichlet卷积 (记作(fg)(x))

(fg)(n)=d|nf(d)g(nd)

引入例题

51nod 1244 莫比乌斯函数之和

题意

i=abμ(i). (2ab1010)

题解

这道题一个裸的杜教筛...求个前缀和,再减一下就行了...

有一个有用的式子d|nμ(d)=[n=1].(这个可以见我之前那篇博客)

解法一:

直接套用之前那个式子....

μ1=ϵ 此处ϵ(x)=[x=1] 也就是上面那个

右边前缀和都很好求i=1nϵ(i)=1.

然后你代入到之前那个式子就有 (令M(i)=i=1nμ(i))

1M(n)=i=2nM(ni)

解法二

我们来手推一下上面的式子qwq

然后就有个很显然的式子(只有i=1时候有贡献)

i=1nd|iμ(d)=1

然后更换枚举项,就是考虑每个d的对于它倍数的贡献,然后交换和式就行了。

i=1nj=1niμ(j)=1

接下来我们就可以提出i=1时的一项,也就是j=1n1=nμ(j)

i=1nμ(i)=1i=2nj=1niμ(j)

我们如果令M(n)=i=1nμ(i)就有和之前那个一样的式子了

M(n)=1i=2nM(ni)

然后我们对于这个式子就可以递归求解了,时间复杂度是Θ(n23).

但是一定要记得要开个哈希表存储一下,不然时间复杂度是个错的!!!(来自ymy大佬的原话)

这个哈希表可以只开n大小,因为ni只有这么多种取值.

代码我看了一下fjzzq大佬的博客 emmm... (同届大佬太恐怖了)

我自己写的一个... 注意一定要全程long long 不然有诡异的错误....

代码

#include <bits/stdc++.h> #define For(i, l, r) for(register ll i = (l), _end_ = (ll)(r); i <= _end_; ++i) #define Fordown(i, r, l) for(register ll i = (r), _end_ = (ll)(l); i >= _end_; --i) #define Set(a, v) memset(a, v, sizeof(a)) using namespace std; typedef long long ll; bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline ll read() { ll x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar() ) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar() ) x = (x<<1) + (x<<3) + (ch ^ '0'); return x * fh; } void File() { #ifdef zjp_shadow freopen ("1244.in", "r", stdin); freopen ("1244.out", "w", stdout); #endif } const int N = 2e6 + 1e3; ll mu[N], Limit, prime[N], cnt; ll summu[N]; bitset<N> is_prime; void Mu_Init(int maxn) { mu[1] = 1; int res; is_prime.set(); is_prime[0] = is_prime[1] = false; For (i, 2, maxn) { if (is_prime[i]) { prime[++cnt] = i; mu[i] = -1; } For (j, 1, maxn) { res = prime[j] * i; if (res >= maxn) break ; is_prime[res] = false; if (i % prime[j]) mu[res] = -mu[i]; else { mu[res] = 0; break ; } } } For (i, 1, maxn) summu[i] = summu[i - 1] + mu[i]; } ll Size; ll p1[N], p2[N], n; inline ll &App(ll x) { if (x < Size) return p1[x]; return p2[n / x]; } inline ll Mu_Sum_(ll x) { if (x <= Limit) return summu[x]; ll &htr = App(x); if (htr != p2[0]) return htr; ll res = 1, Nextx; For (i, 2, x) { Nextx = x / (x / i); res -= 1ll * (Nextx - i + 1) * Mu_Sum_(x / i); i = Nextx; } return htr = res; } inline ll Mu_Sum(ll x) { Size = (int)(sqrt(x) + 0.5); Set(p1, -2333); Set(p2, -2333); n = x; return Mu_Sum_(x); } int main () { File(); ll a, b; cin >> a >> b; Limit = N - (int)1e3; Mu_Init(Limit); cout << Mu_Sum(b) - Mu_Sum(a - 1) << endl; //cerr << "Time used: " << (double)clock() / CLOCKS_PER_SEC << "s" << endl; return 0; }

BZOJ3944 : Sum

然后在简单讲一下求φ(欧拉函数)的前缀和吧...

题意 :

i=1nφ(i)i=1nμ(i). (n2311)

题解 :

其实和上一道题差不多啦...多求了一个φ嘛...

只要知道φ也有一个类似的结论d|nφ(d)=n

这个我不会证明 只有一个感性理解的方法...(来自具体数学)

你考虑所有分母为n的真分数和nn(共有n个)

将分子和分母约去后,其他每个d|nd都对它具有φ(d)的贡献咯...

例如n=6.

16,26,36,46,56,66

约分后就是

16,13,12,23,56,11

这个就比较显然了, 应该可以写出证明的...(太懒也太菜了..)

然后像上一题一样的啦~ 上道题两个解法都能用用...

φ1=id (id(x)=x) 我们令ϕ(n)=i=1nφ(i).

就有一个显然的式子

n(n+1)2ϕ(n)=i=2nϕ(ni)

这个同上就行咯...

hihoCoder #1456 : Rikka with Lattice

这题也是一道好题!~

如果想看的,就在我另一篇博客上 无耻骗点击...


__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/8491542.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(1445)  评论(5编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示