杜教筛
杜教筛
假设f是积性函数,求解:
n∑i=1f(i)很显然,你可以利用线性筛(欧拉筛)以O(n)的时间复杂度计算f(1),f(2),…,f(n)。之后加总就可以了,总的时间复杂度为O(n)。
但是这在n特别巨大的情况下,就很难完成,比如n=1012。
接下来我们就需要引出杜教筛,它可以以O(n23)的时空复杂度完成计算。
首先我们需要引入一个精心挑选的积性函数g,记h=f∗g,其中∗操作表示的是狄里克雷卷积。因此有
h(n)=∑d|nf(d)g(n/d)很显然h也是积性函数。我们简单记S(n)=∑ni=1f(i)。由于
n∑i=1h(n)=n∑i=1∑d|if(d)g(n/d)=n∑d=1g(d)⌊n/d⌋∑i=1f(n/d)=n∑d=1g(d)S(⌊n/d⌋)将右式中g(1)S(n)提取出来得到:
g(1)S(n)=n∑i=1h(n)−n∑d=2g(d)S(⌊n/d⌋)如果函数h和g的连续和的计算都能以O(1)时间复杂度完成。那么我们可以利用数论分块和打表(利用哈希表)的技巧快速求解。
首先我们要理解⌊⌊ab⌋c⌋=⌊abc⌋。记S(⌊n/i⌋)为S(n)的依赖集合,其中i=1,2,…,n。对于任意j≤⌊ni⌋,由于⌊⌊ni⌋j⌋=⌊nij⌋,因此可知S(⌊n/i⌋)的依赖集合是S(n)的依赖集合的子集。因此我们只需要从大到小遍历i,计算S(⌊n/i⌋)值,就可以得到S(n)的整个依赖集合。
注意到⌊n1⌋⌊n2⌋,…,⌊nn⌋集合的大小为O(√n),而计算S(n)仅需要利用仅需要利用S(⌊n1⌋)S(⌊n2⌋),…,S(⌊nn⌋)中的值的加总。因此可以得出时间复杂度:
√n∑i=1O(√ni)=O(∫√n1√nxdx)=O(√n[x1/2]√n1)=O(n34)假如我们利用线性筛计算了前B条记录:S(⌊n1⌋)S(⌊n2⌋),…,S(⌊nB⌋)。那么总的时间复杂度为
O(B)+n/B∑i=1O(√ni)=O(B)+O(∫n/B1√nxdx)=O(B+√n[x1/2]n/B1)=O(B+n√B)选取B=n2/3,可以得到最优的时间复杂度O(n2/3)。很显然需要的空间复杂度就是一开始通过线性筛打表的部分,也是O(n2/3)。