浅谈算法——杜教筛

首先我们给道题目:求\(\sum\limits_{i=1}^n\mu(i)\)

\(n\leqslant 10^5\),我会\(O(n\sqrt{n})\)

\(n\leqslant 10^7\),我会\(O(n)\)线筛!

\(n\leqslant 10^9\),我……

于是杜教筛就此被发明,它可以在\(O(n^{\frac{2}{3}})\)的时间内求出一些积性函数函数的前缀和,如何做?

假定我们现在要求\(S(n)=\sum\limits_{i=1}^nf(i)\),于是我们找来一个积性函数\(g(i)\)(不知道是什么东西),和\(f(i)\)卷积一下,有

\[(f*g)(n)=\sum\limits_{d|n}g(d)f(\dfrac{n}{d})\]

然后我们求一下卷积形式的前缀和

\[\sum\limits_{i=1}^n(f*g)(i)=\sum\limits_{i=1}^n\sum\limits_{d|i}g(d)f(\dfrac{i}{d})\]

然后我们调整一下枚举顺序,得到

\[\sum\limits_{d=1}^ng(d)\sum\limits_{d|i}f(\dfrac{i}{d})\]

\[\sum\limits_{d=1}^ng(d)\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}f(i)\]

\[\sum\limits_{d=1}^ng(d)S(\lfloor\dfrac{n}{d}\rfloor)\]

好,我们现在得到\(\sum\limits_{i=1}^n(f*g)(i)=\sum\limits_{d=1}^ng(d)S(\lfloor\dfrac{n}{d}\rfloor)\),然后我们把这个先放着,稍微思考一下,显然有如下式子

\[g(1)S(n)=\sum\limits_{i=1}^ng(i)S(\lfloor\dfrac{n}{i}\rfloor)-\sum\limits_{i=2}^ng(i)S(\lfloor\dfrac{n}{i}\rfloor)\]

然后你发现中间一个和我们之前推出来的形式是一样的,于是有

\[g(1)S(n)=\sum\limits_{i=1}^n(f*g)(i)-\sum\limits_{i=2}^ng(i)S(\lfloor\dfrac{n}{i}\rfloor)\]

如果中间卷积部分的前缀和非常好算,那么我们就可以对后面那部分进行数论分块,然后递归处理,记得记忆化!

我们回到栗子,题目要求的\(f\)就是\(\mu\)对吧,代回去有

\[g(1)S(n)=\sum\limits_{i=1}^n(\mu*g)(i)-\sum\limits_{i=2}^ng(i)S(\lfloor\dfrac{n}{i}\rfloor)\]

现在我们需要找一个优秀的\(g\),使得他们狄利克雷卷积的前缀和非常好算

我们知道

\[\sum\limits_{d|n}\mu(d)=[n=1]=e\]

所以\((1*\mu)=e\),你说\(e\)的前缀和是啥,当然是1啦,于是我们取\(g(x)=1\),有

\[S(n)=1-\sum\limits_{i=2}^nS(\lfloor\dfrac{n}{i}\rfloor)\]

然后我们线筛出一部分\(\mu\)的前缀和,再来一波记忆化搜索,做完了


再举个栗子,把\(\mu\)换成\(\varphi\)该怎么做?

因为有

\[\sum\limits_{d|n}\varphi(d)=i=id(i)\]

所以还是取\(g(x)=1\),那么得到

\(S(n)=\sum\limits_{i=1}^ni-\sum\limits_{i=2}^nS(\lfloor\dfrac{n}{i}\rfloor)\)

等差数列前缀和\(O(1)\)求出就好了

对于不同的\(f\),只要找到合适的\(g\),就就可以让你的程序变得非常好写

不会找没关系,打个表解决一切

不过记得不要有事没事想着杜教筛,线筛啥的,埃氏筛法也是很有用的,\(O(n\ln n)\)枚举倍数也挺好的

骗分过样例,暴力出奇迹


扯了这么多,讲下时间复杂度证明吧

其实可以发现除了\(S(n)=\sum\limits_{i=2}^nS(\lfloor\dfrac{n}{i}\rfloor)\)贡献了复杂度外,其他基本上可以\(O(1)\)算出答案(恶心的\(g(n)\)不考虑)

那么我们只要算出\(\sqrt n\)\(S(\lfloor\dfrac{n}{i}\rfloor)\)的值即可算出\(S(n)\),于是我们设\(T(n)\)为计算出\(S(n)\)的复杂度,那么有

\[T(n)=O(\sqrt n)+\sum\limits_{i=1}^{\sqrt n}(T(i)+T(\dfrac{n}{i}))\]

其中\(O(\sqrt n)\)是累加合并的时间

然后我们展开一层,因为更深层的复杂度是高阶小量,所以有

\[T(n)=\sum\limits_{i=1}^{\sqrt n}O(\sqrt i)+O(\sqrt{\dfrac{n}{i}})=O(n^{\frac{3}{4}})\]

但由于\(S(n)\)本身是可以通过线筛求出一部分的,假定我们预处理了前\(k\)个的值,且\(k\geqslant \sqrt n\),则复杂度变为

\[T(n)=\sum\limits_{i=1}^{\frac{n}{k}}\sqrt{\dfrac{n}{i}}=O(\dfrac{n}{\sqrt k})\]

则当\(k\)\(n^{\frac{2}{3}}\)时可以取到较好的复杂度\(T(n)=O(n^{\frac{2}{3}})\)

猜你喜欢

转载自www.cnblogs.com/Wolfycz/p/10266081.html