杜教筛
这个东西已经咕了差不多半年QAQ
然后现在才开始写.
有的时候,我们需要完成这样一个问题.
求(sum_{i=1}^nf(i)).其中(f)是积性函数.
当(nleq 10^7)时,可以用线性筛解决这个问题.
然而,起源于(Project Eular)的这个黑科技可以把(n)的范围扩展到(10^{11})左右.
考虑狄利克雷卷积.
假设(f*g=h),那么写成狄利克雷卷积的形式,就是
[sum_{i=1}^nh(i)=sum_{i=1}^nsum_{d|i}f(i)g(frac{i}{d})\
=sum_{d=1}^ng(d)sum_{i=1}^{lfloorfrac{n}{d}
floor}f(i)\
=sum_{d=1}^ng(d)S(lfloorfrac{n}{d}
floor)\
把第一项拉出来,得\
g(1)S(n)=sumlimits_{i=1}^{n}g(i)S(lfloorfrac{n}{i}
floor)-sumlimits_{i=2}^{n}g(i)S(lfloorfrac{n}{i}
floor)
]
考虑到(lfloorfrac{n}{d}
floor)只有(sqrt n)种取值,于是记忆化搜索一波,美滋滋.
因此,只要可以快速求(h)的前缀和与(g),就可以快速求(f)的和.
时间复杂度大约是(O(n^{frac{3}{4}})),可以用积分证.
下面举几个栗子.
莫比乌斯函数
我们知道(mu*1=e),而(sum h=1),因此就很好求啦.
在存储上也有一个小优化,无需(map).考虑到求出的每个(S(x)),(x)都是(n)的因数且(frac{n}{x})不大,因此只需如下代码即可.
LL calc_miu(int x){
if(x<maxn)return miu[x];
else if(w2[n/x])return w2[n/x];
LL ans=1;
for(int i=2,ed;i<=x;i=ed+1)
ed=x/(x/i),ans-=(ed-i+1)*calc_miu(x/i);
return w2[n/x]=ans;
}
欧拉函数
我们知道(varphi*1=id),因此(sum h=frac{n*(n-1)}{2}),就很好求啦.
代码如下
LL calc_phi(int x){
if(x<maxn)return phi[x];
else if(w1[n/x])return w1[n/x];
LL ans=(LL)x*(LL)(x+1)/2;
for(int i=2,ed;i<=x;i=ed+1)
ed=x/(x/i),ans-=(ed-i+1)*calc_phi(x/i);
return w1[n/x]=ans;
}
Min_25筛
待UPD