zoukankan      html  css  js  c++  java
  • 浅谈算法——杜教筛

    首先我们给道题目:求(sumlimits_{i=1}^nmu(i))

    (nleqslant 10^5),我会(O(nsqrt{n}))

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

    (nleqslant 10^9),我……

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

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

    [(f*g)(n)=sumlimits_{d|n}g(d)f(dfrac{n}{d}) ]

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

    [sumlimits_{i=1}^n(f*g)(i)=sumlimits_{i=1}^nsumlimits_{d|i}g(d)f(dfrac{i}{d}) ]

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

    [sumlimits_{d=1}^ng(d)sumlimits_{d|i}f(dfrac{i}{d}) ]

    [sumlimits_{d=1}^ng(d)sumlimits_{i=1}^{lfloorfrac{n}{d} floor}f(i) ]

    [sumlimits_{d=1}^ng(d)S(lfloordfrac{n}{d} floor) ]

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

    [g(1)S(n)=sumlimits_{i=1}^ng(i)S(lfloordfrac{n}{i} floor)-sumlimits_{i=2}^ng(i)S(lfloordfrac{n}{i} floor) ]

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

    [g(1)S(n)=sumlimits_{i=1}^n(f*g)(i)-sumlimits_{i=2}^ng(i)S(lfloordfrac{n}{i} floor) ]

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

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

    [g(1)S(n)=sumlimits_{i=1}^n(mu*g)(i)-sumlimits_{i=2}^ng(i)S(lfloordfrac{n}{i} floor) ]

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

    我们知道

    [sumlimits_{d|n}mu(d)=[n=1]=e ]

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

    [S(n)=1-sumlimits_{i=2}^nS(lfloordfrac{n}{i} floor) ]

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


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

    因为有

    [sumlimits_{d|n}varphi(d)=i=id(i) ]

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

    (S(n)=sumlimits_{i=1}^ni-sumlimits_{i=2}^nS(lfloordfrac{n}{i} floor))

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

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

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

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

    骗分过样例,暴力出奇迹


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

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

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

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

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

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

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

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

    [T(n)=sumlimits_{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}}))

  • 相关阅读:
    Oracle的数据库,实例,服务名
    ubuntu14.04安装oracle 11g
    sqlldr导入导出需要注意的问题
    EOF字符串
    登录plsql developer时候出现连接串问题导致的下拉列表中没有出现tnsnames.ora文件中配置的那些服务
    Oracle导出数据中的prompt,set feedback 等是什么意思
    怎么给普通用户付给权限,让它能读取v$session表
    Oracle数据库状态
    管理子程序
    pl/sql developer安装与配置
  • 原文地址:https://www.cnblogs.com/Wolfycz/p/10266081.html
Copyright © 2011-2022 走看看