zoukankan      html  css  js  c++  java
  • [复习]莫比乌斯反演,杜教筛,min_25筛

    [复习]莫比乌斯反演,杜教筛,min_25筛

    莫比乌斯反演

    做题的时候的常用形式:

    [egin{aligned}g(n)&=sum_{n|d}f(d)\f(n)&=sum_{n|d}mu(frac{d}{n})g(d)end{aligned} ]

    实际上还有

    [egin{aligned}g(n)&=sum_{d|n}f(d)\f(n)&=sum_{d|n}mu(frac{n}{d})g(d)end{aligned} ]

    证明可以看看这里,只需要把式子带回去就可以证了。
    式子很简单,做题很有用,技巧很丰富。

    主要通过题目来写这一部分的内容。

    • 【BZOJ1101】ZAP
      题意:求(displaystyle sum_{i=1}^asum_{j=1}^b[gcd(i,j)==d])
      题解:
      (displaystyle f(d)=sum_{i=1}^asum_{j=1}^b[gcd(i,j)==d],g(n)=sum_{n|d}f(d))
      那么通过式子的含义不难知道(displaystyle g(d)=sum_{i=1}^asum_{j=1}^b[d|gcd(i,j)])
      那么推导就很简单了

    [egin{aligned} g(d)&=sum_{i=1}^asum_{j=1}^b[d|gcd(i,j)]\ &=sum_{i=1}^{a/d}sum_{j=1}^{b/d} [1|gcd(i,j)]\ &= lceil frac{a}{d} ceil* lceil frac{b}{d} ceil\ f(d)&=sum_{d|i}mu(frac{i}{d})g(i)\ &=sum_{d|i}mu(frac{i}{d})lceil frac{a}{i} ceil* lceil frac{b}{i} ceil\ &=sum_{i=1}^{min(a,b)/d}mu(i)lceil frac{a/d}{i} ceil* lceil frac{b/d}{i} ceil\ end{aligned}\ ]

    考虑最终这个求和式,如果直接单次(O(min(a,b)/d))的计算,那么复杂度是(O(Tmin(a,b)/d))的,是会(TLE)的。
    实际上,当(i)([1,n])时,(n/i)向下取整的取值只有大约(2sqrt n)个左右,所以对于相同的值完全可以合并在一起算,所以我们只需要处理出(mu)的前缀和即可直接数论分块计算,把单次复杂度优化到了(sqrt {min(a,b)})级别。

    [egin{aligned} Ans&=sum_{i=1}^nsum_{j=1}^m lcm(i,j)\ &=sum_{i=1}^nsum_{j=1}^m frac{ij}{gcd(i,j)}\ &=sum_{d=1}^nfrac{1}{d}sum_{i=1}^nsum_{j=1}^m [gcd(i,j)=d]ij\ &=sum_{d=1}^ndsum_{i=1}^{n/d}sum_{j=1}^{m/d} [gcd(i,j)=1]ij end{aligned}]

    (displaystyle f(d,n,m)=sum_{i=1}^nsum_{j=1}^m[gcd(i,j)=d]ij,g(n)=sum_{n|d}f(d))

    [egin{aligned} g(d)&=sum_{i=1}^nsum_{j=1}^m[d|gcd(i,j)]ij\ &=d^2sum_{i=1}^{n/d}sum_{j=1}^{m/d}ij\ &=d^2 S([frac{n}{d}])S([frac{m}{d}]) end{aligned}]

    其中(displaystyle S(n)=sum_{i=1}^ni=frac{n(n+1)}{2})
    那么题目中的式子要求的是(displaystyle f(1)=sum_{i=1}^nmu(i)g(i))
    带回到式子中去,可以得到:

    [egin{aligned} Ans&=sum_{d=1}^ndsum_{i=1}^{n/d}sum_{j=1}^{m/d} [gcd(i,j)=1]ij\ &=sum_{d=1}^ndsum_{i=1}^{n/d}mu(i)i^2S([frac{n}{id}])S([frac{m}{id}]) end{aligned}]

    到了这一步,可以做到(O(n))的复杂度了,提前预处理后面整块的前缀和,然后数论分块计算答案即可。
    但是如果要复杂度更加优秀的话,我们需要继续推导。

    [egin{aligned} Ans&=sum_{d=1}^ndsum_{i=1}^{n/d}mu(i)i^2S([frac{n}{id}])S([frac{m}{id}])\ &=sum_{T=1}^nS([frac{n}{T}])S([frac{m}{T}])sum_{d|T,id=T}dmu(i)i^2\ &=sum_{T=1}^nS([frac{n}{T}])S([frac{m}{T}])sum_{i|T}frac{T}{i}mu(i)i^2\ &=sum_{T=1}^nS([frac{n}{T}])S([frac{m}{T}])Tsum_{i|T}imu(i) end{aligned}]

    不难发现最后那一部分是一个积性函数,那么(displaystyle Tsum_{i|T}imu(i)),可以在线性时间里面筛出值并算出前缀和,所以对于前半部分数论分块,可以做到单次询问(O(sqrt n))
    考虑这里比前面单词询问线性的优化在了哪里,之前那个的确可以做到回答答案是根号级别的,但是因为每次的(n,m)的值都不同,导致不得不需要对于每一次不同的询问做一次前缀和,而后面那个则每次需要预处理的东西都是一样的,因此只需要全局做一次预处理即可。
    想想这个操作的本质,其实就是数论分块的对象不同而已,因此当有多个和式出现的时候,要考虑清楚到底对于谁进行数论分块,能够达到最好的效果。

    现在可以来总结一下。
    我们仔细想想,这类题目的难点在哪里呢?
    首先是推式子,但是这个熟练之后很简单。注意对于不同的东西数论分块可以得到不同的复杂度。
    其次就是为了数论分块求函数的前缀和了。其实这个算不上太难,如果数据范围到了(1e7),显然只能线性筛,那就通过线性筛的本质来考虑,如果当前加入的是一个从未出现过的质因子,那么直接乘积即可,否则的话考虑额外加入一个重复质因子时的贡献。对于赋初值而言,只需要单独考虑质数的贡献就好了。如果数据范围并没有到达(1e7),如果在(1e6)的范围内,不要一味考虑线性筛,这个复杂度下,调和级数的埃氏筛也是可行的。同时要对于一些积性函数的狄利克雷卷积有所了解,这样推式子的时候会方便很多。
    这里也提供几个比较常用的式子。
    (1)表示常数,即(1(i)=1)
    (e)表示单位元,(e(i)=[i==1])
    (id)表示这个数本身,(id(i)=i)
    (mu,varphi)就不说了。

    • (displaystyle (1*mu)(n)=sum_{d|n}mu(d)=e)
      证明并不难,(mu)考虑的只有单个质因子,当某个质因子出现超过了(1)(mu)的值就是(0)。那么我们假设(displaystyle n=prod_{i=1}^k p_i^{a_i})。那么我们真正有用的值只有(2^k)个。当(k>0)的时候,显然(mu=1)(mu=-1)是一一对应的,因此和为(0),当(k=0)的时候,(n=1),此时结果为(1)。因此这两个的狄利克雷卷积就是单位元。

    • (displaystyle (mu*id)(n)=sum_{d|n}dmu(frac{n}{d})=varphi(n))
      其实换种写法就很好证明了,(displaystyle sum_{d|n}frac{n}{d}mu(d))
      (mu)函数又可以称为容斥系数,那么这鬼玩意的本质就是(n)减去其大于(1)的约数的倍数,也就是与(n)互质的数的个数,也就是(varphi)

    • (displaystyle (1*varphi)(n)=sum_{d|n}varphi(d)=id(n)=id)
      可以用狄利克雷卷积的方法证明:(displaystyle1*varphi=1*mu*id=e*id=id)

    • (displaystyle sum_{i=1}^n [gcd(i,n)=1]i)
      即小于(n)并且与(n)互质的数之和。除了莫比乌斯反演可以推之外,还有一个很好的推导方法。假设(x)(n)互质,那么(n-x)(n)互质,因此所有与(n)互质的数可以两两配对,且和为(n)。因此上述式子等于(displaystyle frac{nvarphi(n)}{2})

    杜教筛

    求解积性函数前缀和。
    假设给定的积性函数为(f(x)),设它的前缀和(displaystyle S(n)=sum_{i=1}^n f(i))
    显然不能直接求解我们才会单独把它拿出来考虑。
    既然不能直接求解的话,肯定要找一个什么东西来过度,假装我们找到了一个积性函数(g(x))
    把两者卷积起来:

    [(g*f)(n)=sum_{d|n}g(d)*f(frac{n}{d}) ]

    然后考虑求解这个玩意的前缀和:

    [egin{aligned} sum_{i=1}^n (g*f)(i)&=sum_{i=1}^nsum_{d|i}g(d)f(frac{i}{d})\ &=sum_{d=1}^ng(d)sum_{j=1}^{n/d}f(i)\ &=sum_{d=1}^ng(d)S([frac{n}{d}]) end{aligned}]

    然后我们惊奇的发现有这么一个式子:

    [egin{aligned} g(1)S(n)&=sum_{i=1}^ng(i)S([frac{n}{i}])-sum_{i=2}^ng(i)S([frac{n}{i}])\ &=sum_{i=1}^n (f*g)(i)-sum_{i=2}^n g(i)S(frac{n}{i}) end{aligned}]

    那么假装我们可以快速的计算((f*g))的前缀和,那么后半部分可以数论分块+递归求解。
    这样一来我们就可以还原出(S(n))了。
    所以。。。实际上杜教筛就是一个构造的过程。
    那么能够快速计算前缀和要有多好求呢?比如说我们要求(mu)的前缀和,那么最好可以(O(1))求,而(mu*1=e)(e)的前缀和就是(1),那就很好办了。令(f(x)=mu(x),g(x)=1),可以得到

    [egin{aligned} S(n)&=1-sum_{i=2}^nS([frac{n}{i}]) end{aligned}]

    这个东西?递归算算就好了。当然了,别少了记忆化。
    推式子熟练了就很好了,而构造(g)的时候就记住前面讲的几个常用的狄利克雷卷积的式子,往那个上面靠。这里随便推一道题目可以看看是怎么构造的。

    [egin{aligned} Ans&=sum_{i=1}^nsum_{j=1}^nijgcd(i,j)\ &=sum_{d=1}^n d^3sum_{i=1}^{n/d}sum_{j=1}^{n/d}ij[gcd(i,j)=1]\ &=sum_{d=1}^n d^3sum_{i=1}^{n/d}mu(i)*i^2*S^2([frac{n}{id}])\ &=sum_{T=1}^n S^2([frac{n}{T}])sum_{d|T} d^3(frac{T}{d})^2mu(frac{T}{d})\ &=sum_{T=1}^n S^2([frac{n}{T}])(T^2sum_{d|T}dmu(frac{T}{d})\ &=sum_{T=1}^n S^2([frac{n}{T}])(T^2varphi(T))\ end{aligned}]

    考虑怎么求(f(T)=T^2varphi(T))的前缀和,这个东西显然是一个积性函数。
    发现式子中带(varphi),所以杜教筛配(g)的时候往(displaystyle sum_{d|T}varphi(T)=T)上面靠。

    [egin{aligned} (f*g)(n)&=sum_{d|n}f(d)g(frac{n}{d})\ &=sum_{d|n}d^2varphi(d)g(frac{n}{d})\ end{aligned}]

    因为要往只有(varphi)上靠,所以令(g(x)=x^2)

    [egin{aligned} (f*g)(n)&=sum_{d|n}d^2varphi(d)g(frac{n}{d})\ &=n^2sum_{d|n}varphi(d)\ &=n^3 end{aligned}]

    那就很好办了,(displaystyle sum_{i=1}^n i^3=(1+2+...+n)^2)可以(O(1))计算,那么直接杜教筛就可以快速计算前缀和了。

    那么杜教筛就是一个具有很强的构造性的东西,这也从一些方面上使得其并不是那么的实用。

    min_25筛

    (min\_25)筛用于计算积性函数前缀和(displaystyle S(n)=sum_{i=1}^n f(i))
    相比于杜教筛而言,(min\_25)筛的题目更加灵活多变
    所求的积性函数要满足两个条件

    • (f(x))(x)为质数的时候要存在一个多项式的表示方法。
    • (f(x^c))(x)为质数的时候能够快速计算。

    至于为什么是这两个条件,与(min\_25)筛的过程之间有着密切的联系。

    既然上述的条件中,我们已经把质数单独分割出来了,那么我们应该明白,(min\_25)筛的过程是一个分解过程,即把所有数分成两类,质数以及合数,当然还有特殊的(1)
    这里提前说明几个东西,底下直接把(P)定义为质数集合,(P_i)表示的是第(i)大的质数。

    • 质数部分
      (g(n,j))表示的是,小于等于(n)的所有数(i)中,其最小质因子(pgt P_j)或者(i)本身就是质数的所有的(i)(f(i))之和。
      等等,前面不是说了只有当(i)是质数的时候才能方便的计算(f(i))吗?别急,这里我们假装所有数都是质数,带入到是质数的式子中一起计算,换句话说,这里就是一个构造过程,不需要管那么多啦QwQ。或者这样想,如果我们知道了(g(n,infty)),显然这个东西就是所有质数的和啊QaQ。接下来往下面看吧。
      考虑一下这个东西怎么样才能转移呢,显然我们的转移从(j-1)转移到了(j),那么就考虑(g(n,j-1))(g(n,j))多算了些什么。
      多算的部分显然就是那些最小质因子恰好为(P_{j})的数(别忘了上面那个是大于号),那么最小的一个最小质因子为(P_{j})的数是谁呢?(P_{j}^2)。如果(P_j^2)(n)都要大了,那么我们什么也没有减掉,意味着(g(n,j)=g(n,j-1))
      否则的话,想想我们减掉了什么呢?我们先给所有数除掉一个(P_j),那么如果剩下的部分的最小质因子还小于等于(P_j)的话那么显然不合法,也就是(displaystyle g([frac{n}{P_j}],j-1)),但是这样子减多了,把小于(P_j)的质数的贡献给减掉了,这些质数与(P_j)构成的合数的最小质因子小于(P_j),显然是早就被减掉了。那么我们重新把第(1)个到第(j-1)个质数的贡献加回来,也就是(g(P_j-1,j-1))
      然后我们要求的是(g)的和,我们减去的是(P_j)因子的贡献,而(f(x))为积性函数,所以转移可以写成:

    [g(n,j)=egin{cases} g(n,j-1)&P_j^2>n\ g(n,j-1)-f(P_j)(g([frac{n}{P_j}],j-1)-g(P_j-1,j-1))&P_j^2leq n end{cases}]

    前面也说了,(f(x))(x)为质数的情况下可以写成一个多项式的形式,所以其实这个求(g)的本质上可以理解为求(f(x)=x^k)的一个过程。同时,每次都是从(j-1)转移到(j),仔细思考,这个过程很类似于埃氏筛法,每次筛去了一个质数的所有倍数的贡献。
    那么如果最终要计算出所有质数的贡献,那么显然就是(g(n,infty)),事实上我们只需要算到(sqrt n) 以内的所有质数就行了。同时,因为上述转移的过程中是整除,其实在计算过程中的(n)的取值也只有(2sqrt n)个左右,也只需要离散后记录这些位置就行了。
    (g)的初值也就是(g(n,0))自己想想怎么计算吧QwQ。

    • 合数部分
      知道了质数的贡献,而目标函数又是积性函数,所以我们只需要用所有的质数拼出所有的合数就可以计算答案了。
      (S(n,j))表示所有最小质因子大于等于(P_j)的数(i)(f(i))的和,注意这里和上面(g)的描述的区别。
      那么计算(S)的值的时候显然是先把质数的贡献给算上,这一部分的贡献是(displaystyle g(n,|P|)-sum_{i=1}^{j-1}f(P_j)),其中(P)是小于等于(sqrt n)的质数集合。
      接下来考虑合数的贡献,显然每个合数都存在一个最小质因子,那么我们来枚举这个因子,假设为(P_k,kgeq j),枚举其幂,假设为(e),那么考虑的就是所有包含了(P_k^e)的合数的贡献。
      因为(f)是积性函数,所以把这里每次就考虑只包含(P_k^e)的贡献,首先是(displaystyle S([frac{n}{P_k^e}],k+1)),因为这里并没有包含(1)的贡献,意味着(P_k^e)本身的贡献没有计算进来,所以要额外加进来。
      所以转移就可以写成:

    [S(n,j)=g(n,|P|)-sum_{i=1}^{j-1}f(P_i)+sum_{kgeq j}^{|P|}sum_{e}(f(P_k^e)S([frac{n}{P_k^e}],k+1)+f(P_{k}^{e+1})) ]

    所以底下就来丢几道题目吧。。。

    • 【LOJ#6053】简单的函数
      发现这个东西是(f(p^k)=poplus k),对于质数而言(f(p)=p-1),当然(2)比较特殊(f(2)=p+1=3),所以我们先把它当成(p-1)算,最后再加上(2)就好了。
      前面也说了,因为在质数上涉及到的是一个多项式计算,所以本质上要求解的就是(f(p)=p^k)的形式,那么从这个式子就知道我么要求的是(k=0)(k=1)的情况。
      (g(n,j))表示(k=1)的情况。即(f(p)=p),那么显然有转移:

    [g(n,j)=egin{cases} g(n,j-1)&P_j^2>n\ g(n,j-1)-P_j(g([frac{n}{P_j}],j-1)-g(P_j-1,j-1))&P_j^2le n end{cases}]

    (h(n,j))表示(k=0)的情况,即(f(p)=1)时的计算,那么有转移:

    [h(n,j)=egin{cases} h(n,j-1)&P_j^2>n\ h(n,j-1)-(h([frac{n}{P_j}],j-1)-h(P_j-1,j-1))&P_j^2le n end{cases}]

    显然有(g(n,|p|)-h(n,|p|))为所有质数的答案,假设其为(F(n))
    那么设(S(n,j))表示(displaystyle sum_{i=2}^n f(i)),其中(i)的最小质因子大于等于(P_j)
    得到转移:

    [S(n,j)=F(n)-sum_{i=1}^{j-1}f(P_j)+sum_{kgeq j}sum_e(f(P_k^e)S([frac{n}{P_k^e}],k+1)+f(P_k^{e+1})) ]

    那么简单的递归处理即可。
    讲讲几个实现的地方,首先要筛的质数只有(sqrt n)以内的,所以完全可以把他们的(f)的前缀和给求出来,这也就是(g)(S)中转移看起来不好算的两部分的计算方法。其次,计算(g(n,j))的时候,真正有用的(n)只有(2sqrt n)个,因为这个过程你可以看成一个数论分块的过程,因此提前把(n)给数论分块,得到所有有用的(n),这样子可以节约大量空间。第三,发现(g)的转移的中,之和(j-1)相关,意味着在开数组的时候只需要开一维,第二维可以滚掉,并且实际上最终(g)要用的也只有(g(n,|P|)),所以滚掉就好了。最后一个,计算(S)的时候不需要记忆化。
    这题代码我重写了一遍,直接戳LOJ的提交记录吧

    • 【UOJ#188】sanrd
      (displaystyle sum_{i=l}^r f(i))的值,其中(f(i))表示(i)的次大质因子。
      看起来这里的这个函数与质数无关,并且也不是一个积性函数了,那么怎么办呢?
      首先实际上我们要求的还是这个函数的前缀和,考虑一下(min\_25)筛最终求解答案的过程,每次我们枚举其当前拥有的最小质因子。那么,通过当前计算的(S(n,j))(j),我们可以很容易的知道当前数的上一个质因子是(P_{j-1})。那么如果以(P_{j-1})为质因数为贡献的话,显然就是当前剩下的数中质数的个数,这个可以提前用(min\_25)的前半部分筛出来。否则以更大的质数为贡献,那么枚举当前的最小质因子把它除掉接着递归处理即可,注意(P_k^c)的这个数的次大质因子为(P_k),这里的贡献是额外算的。
      代码直接戳UOJ记录
  • 相关阅读:
    Kafka:ZK+Kafka+Spark Streaming集群环境搭建(二十六)Structured Streaming:WARN clients.NetworkClient: Error while fetching metadata with correlation id 1 : {my-topic=LEADER_NOT_AVAILABLE}
    Kafka:ZK+Kafka+Spark Streaming集群环境搭建(二十五)Structured Streaming:同一个topic中包含一组数据的多个部分,按照key它们拼接为一条记录(以及遇到的问题)。
    Kafka:ZK+Kafka+Spark Streaming集群环境搭建(二十四)Structured Streaming:Encoder
    Kafka:ZK+Kafka+Spark Streaming集群环境搭建(二十三)Structured Streaming遇到问题:Set(TopicName-0) are gone. Some data may have been missed
    Structured Streaming编程向导
    Kafka:ZK+Kafka+Spark Streaming集群环境搭建(二十二)Spark Streaming接收流数据及使用窗口函数
    Linux:磁盘挂载
    Kafka:ZK+Kafka+Spark Streaming集群环境搭建(二十一)NIFI1.7.1安装
    Spark参数设置的方式
    mydumper安装、原理介绍
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10169190.html
Copyright © 2011-2022 走看看