杜教筛
先来一道水题
求Σi=1nμ(i)
n<=1e5 我会暴力!!
n<=1e7我会线性筛!!
n<=1e9我……WCNM!!
众所周知,线性筛它死了
这个时候,我们的杜教筛大哥走了过来,说:让我来!
杜教筛是干什么用的呢?是用来求积性函数前缀和的
时间复杂度O(n^(2/3))
我们假设有一个积性函数f
设Σi=1nf(i)=s(i)
设f*g=h,f,g,h是积性函数
考虑Σi=1nh(i)
Σi=1nh(i)=Σi=1nΣd|ig(d)*f(i/d)
更换枚举顺序,Σi=1nh(i)=Σd=1ng(d)*Σd|inf(i/d)
设i'=i/d,i=i'*d
则原式可化为Σi=1nh(i)=Σd=1ng(d)*Σi'=1⌊n/d⌋f(i')
后面那个Σ=s(⌊n/d⌋)
所以原式可化为Σi=1nh(i)=Σd=1ng(d)*s(⌊n/d⌋)
将右边第一项提出来,Σi=1nh(i)=Σd=2ng(d)*s(⌊n/d⌋)+g(1)*s(n)
所以,g(1)*s(n)=Σi=1nh(i)-Σd=2ng(d)*s(⌊n/d⌋)
如果Σi=1nh(i)可以O(1)求的话,右边数论分块再加上记忆搜,是可以很快求出答案的
具体的话,如果用线性筛筛出来前n2/3个之后,再用上式带入计算,总时间复杂度就是O(n2/3)不会证233
我们知道,记忆化是要开数组的,显然n<=1e9它不让你开数组,那怎么办呢??
方法1:我会哈希!!那你比较勤劳
方法2:我会map!!那你多个log
可是当你遇到了像david-alwal这样既不勤劳又不想要log的人怎么办呢
方法3:我们有黑科技unordered_map!!
关于unordered_map的几点注意事项:
1、万能库----bits/stdc++.h----它死了!!!@Th_Au_K
我们需要开一个库,#include<tr1/unordered_map>
2、声明变量的时候,tr1::unordered_map<int,int>M;
剩下它就和map一样了,除了复杂度,它少了一个log
举几个晓栗子
eg1:f=μ
因为μ*I=e
e的前缀和极其好求就是1,所以g=I,h=e就好了
eg2:f=φ
因为φ*I=id
id的前缀和是n*(n+1)/2
所以g=I,h=id
eg3:f(n)=n*φ(n)
不那么显然了
h=f*g
那么,h(n)=(f*g)(n)=Σd|nf(d)*g(n/d)=Σd|nφ(d)*d*g(n/d)
那个d很不爽,所以我们考虑g=id
那么,原式=Σd|nφ(d)*d*n/d=Σd|nφ(d)*n
将n提出去,原式=n*Σd|nφ(d)=n2
即f(n)=n*φ(n),g(n)=n,h(n)=n2即可