曾经学过,但当时只是浅薄了理解了一下,并没有学会实际应用,所以又来学了一遍
——前言
前置定义
数论函数
积性函数
狄利克雷卷积
常见积性函数
[egin{aligned}
1(n)&=1\
epsilon(n)&=[n=1]\
sigma_k(n)&=sum_{i|n}i^k\
id_k(n)&=n^k\
varphi(n)&=sum_i^n[gcd(i,n)=1]\
mu(n)&=
left{egin{matrix}
1&n=1\
(-1)^x&prod_{i=1}^x k_i=1\
0&max{k_i}geq 2
end{matrix}
ight.
end{aligned}]
常见积性函数之间的变换及证明
[egin{aligned}
(mu*1)(n)&=sum_{d|n}mu(d)1(frac{n}{d})\
&=sum_{d|n}mu(d)\
&=[n=1]\
&=epsilon(n)
end{aligned}]
[egin{aligned}
(varphi*1)(n)&=sum_{d|n}varphi(d)1(frac{n}{d})\
&=sum_{d|n}varphi(d)
end{aligned}]
考虑枚举 (d) 表示 (1sim n) 中某个数与 (n) 的 (gcd) 为 (d),那么当 (din[1,n]) 时,得到的 (sum_i^n[gcd(i,n)=d]) 的和就是 (n)
现在考虑如何求 (sum_i^n[gcd(i,n)=d]),把 (n) 拆分成 (n imes frac{n}{d}) 的形式,考虑一个满足条件的 (i(ileq n)),将 (i) 拆分成 (k imes d) 的形式,显然有 (kleq frac{n}{d}),又因为 (gcd(i,n)=d),所以需要满足 (kperp frac{n}{k}),那么满足条件的 (i) 只会有 (varphi(frac{n}{d})) 个,所以容易得到
[egin{aligned}
sum_{d|n}varphi(frac{n}{d})&=id_1(n)\
ecause sum_{d|n}varphi(frac{n}{d})&=sum_{d|n}varphi(d)\
&=(varphi*1)(n)\
herefore(varphi*1)(n)&=id_1(n)
end{aligned}
]
设 (f(d)) 表示 (1sim n) 中与 (n) 的 (gcd) 为 (d) 的个数,(g(d)) 表示 (1sim n) 中与 (n) 的 (gcd) 为 (d) 的倍数的个数
显然有
[g(d)=[d|n]frac{n}{d}
]
由莫比乌斯反演得
[egin{aligned}
f(g)&=sum_{g|d}mu(frac{d}{g})g(d)\
&=sum_{g|d,d|n}mu(frac{d}{g})frac{n}{d}\
f(1)&=sum_{d|n}mu(d)frac{n}{d}
end{aligned}
]
容易发现 (f(1)) 其实就是 (1sim n) 中与 (n) 互质的个数
[egin{aligned}
(mu*id_1)(n)&=sum_{d|n}mu(d)1(frac{n}{d})\
&=f(1)\
&=varphi(n)
end{aligned}
]
杜教筛
杜教筛常用于求解一些数论函数的前缀和,由杜瑜皓杜老师研究并发明
经常与洲阁筛、(min25) 筛相提并论,因其对选手的数学能力要求不高、较为优秀的时间复杂度和代码实现的简短,常作为三大筛法的优先学习对象
下面这个图展示了三个筛法在不同数据范围下的算法效率,容易发现杜教筛的复杂度变化相对于其他两个来说是较为平稳的
基本问题
给定一个数论函数 (f(x)),求出 (f(x)) 的第 (n) 项前缀和 (S(n)),(nleq 10^{10})
算法核心
杜教筛的核心是构造一个数论函数 (g(x)),从而得到 (h=f*g),(h) 的第 (n) 项前缀和很好求出,然后通过一些推导从而求出 (S(n))
通项公式
因为 (f*g) 的前缀和很好求,考虑用它去推得 (S(n))
[sum_{i=1}^n(f*g)(i)=sum_{i=1}^nsum_{d|i}f(d)g(frac{i}{d})
]
然后我们将后面那个狄利克雷卷积的形式改变一下
我们原来的表示是
[a[i]=sum_{d|i}b[d]c[frac{i}{d}]
]
我们现在改成
[a[i]=sum_{i=j imes k}b[j]c[k]
]
显然是正确的吧
然后又因为我们只需要求卷积后每项的和,所以只需要考虑每对 (j,k) 对和的贡献就行了,所以上面推到一半的式子可以转化成
[egin{aligned}
ecause sum_{d=1}^ng(d)sum_{i=1}^{left lfloorfrac{n}{d}
ight
floor} f(i)&=sum_{d=1}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)\
herefore sum_{i=1}^n(f*g)(i)&=sum_{d=1}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)\
g(1)S(n)&=sum_{i=1}^n(f*g)(i)-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)\
S(n)&=frac{sum_{i=1}^n(f*g)(i)-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
end{aligned}
]
最后我们得到了一个 (S(n)) 的递推式,整除分块递归求解即可
算法效率
通常情况下,(sum_{i=1}^n(f*g)(i)) 是可以 (mathcal{O}(1)) 求出的,一段 ([l,r]) 的 (g(d)) 之和也可以 (mathcal{O}(1)) 得到,所以时间复杂度主要集中在递归求解上,而且通常情况下会对 (S(n)) 进行记忆化
由于整除分块的复杂度是 (mathcal{O}(sqrt{n})),而且 (left lfloorfrac{n}{d}
ight
floor) 的取值只有 (sqrt{n}) 个,所以最后的复杂度是
[sum_{i=1}^{sqrt{n}}(i+sqrt{frac{n}{i}})=n^{frac{3}{4}}
]
我们可以通过线性筛先预处理出来较小的 (n) 的 (S(n)), (n) 很小时就没必要再递归下去了,这样复杂度可以优化到 (mathcal{O}(n^{frac{2}{3}}))
算法模板
提前会有一个线性筛,剩下的就只剩一个递归函数了
inline ll F (register ll n) {
if (n <= 3e6) return sumf[n]; // 预处理出 n 较小时的前缀和
if (f[n]) return f[n]; // 记忆化,如果求过这个值,就不需要再递归一遍了
register ll ans = sum (f * g); // 这是 f * g 的 n 项前缀和
for (register ll l = 2, r; l <= n; l = r + 1) // 整除分块
r = n / (n / l), ans -= (sumg[r] - sumg[l - 1]) * F (n / l);
// [l,r] 的 F (n / l) 是一样的,对 g(x) 求个和即可
return f[n] = ans / g[1]; // 别忘了除上 g(1)
}
常见数论函数的前缀和推导
- (f(n)=mu(n),S(n)=sum_{i=1}^{n}mu(i))
构造函数 (g(n)=1(n)),容易得到 (f*g=epsilon)(详见上面证明),套用公式
[egin{aligned}
S(n)&=frac{sum_{i=1}^n(f*g)(i)-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{sum_{i=1}^nepsilon(i)-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{1-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{1-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{1}\
&=1-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)
end{aligned}
]
因为 (g(n)=1(n)),所以 (g(n)) 的前缀和可以直接 (mathcal{O}(1)) 求,就可以直接套用模板做了
- (f(n)=varphi(n),S(n)=sum_{i=1}^{n}varphi(i))
构造函数 (g(n)=1(n)),容易得到 (f*g=id_1)(详见上面证明),套用公式
[egin{aligned}
S(n)&=frac{sum_{i=1}^n(f*g)(i)-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{sum_{i=1}^nid_1(i)-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{frac{n(n+1)}{2}-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{frac{n(n+1)}{2}-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{1}\
&=frac{n(n+1)}{2}-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)
end{aligned}
]
同样可以套用模板去做
- (f(n)=mu(n)n,S(n)=sum_{i=1}^{n}mu(i)i)
构造函数 (g(n)=id_1(n))
[egin{aligned}
(f*g)(i)&=sum_{d|i}f(d)g(frac{i}{d})\
&=sum_{d|i}mu(d)d imes frac{i}{d}\
&=sum_{d|i}mu(d)i\
&=isum_{d|i}mu(d)\
&=i[i=1]\
&=epsilon(i)
end{aligned}
]
接着套用公式
[egin{aligned}
S(n)&=frac{sum_{i=1}^n(f*g)(i)-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{sum_{i=1}^nepsilon(i)-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{1-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{1-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{1}\
&=1-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)
end{aligned}
]
因为 (g(n)=id_1(n)),所以可以 (mathcal{O}(1)) 计算前缀和,同样可以套用模板去做
- (f(n)=varphi(n)n,S(n)=sum_{i=1}^{n}varphi(i)i)
构造函数 (g(n)=id_1(n))
[egin{aligned}
(f*g)(i)&=sum_{d|i}f(d)g(frac{i}{d})\
&=sum_{d|i}varphi(d)d imes frac{i}{d}\
&=sum_{d|i}varphi(d)i\
&=isum_{d|i}varphi(d)\
&=i imes i\
&=i^2
end{aligned}
]
接着套用公式
[egin{aligned}
S(n)&=frac{sum_{i=1}^n(f*g)(i)-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{sum_{i=1}^ni^2-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{frac{n(n+1)(2n+1)}{6}-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{frac{n(n+1)(2n+1)}{6}-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{1}\
&=frac{n(n+1)(2n+1)}{6}-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)
end{aligned}
]
然后套用模板去做就行了
- (f(n)=mu(n)n^2,S(n)=sum_{i=1}^{n}mu(i)i^2)
构造函数 (g(n)=id_2(n))
[egin{aligned}
(f*g)(i)&=sum_{d|i}f(d)g(frac{i}{d})\
&=sum_{d|i}mu(d)d^2 imes frac{i^2}{d^2}\
&=sum_{d|i}mu(d)i^2\
&=i^2sum_{d|i}mu(d)\
&=i^2[i=1]\
&=epsilon(i)
end{aligned}
]
接着套用公式
[egin{aligned}
S(n)&=frac{sum_{i=1}^n(f*g)(i)-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{sum_{i=1}^nepsilon(i)-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{1-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{1-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{1}\
&=1-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)
end{aligned}
]
因为 (g(n)=id_2(n)),所以前缀和直接就是 (frac{n(n+1)(2n+1)}{6}),同样可以套用模板去做
- (f(n)=varphi(n)n^2,S(n)=sum_{i=1}^{n}varphi(i)i^2)
构造函数 (g(n)=id_2(n))
[egin{aligned}
(f*g)(i)&=sum_{d|i}f(d)g(frac{i}{d})\
&=sum_{d|i}varphi(d)d^2 imes frac{i^2}{d^2}\
&=sum_{d|i}varphi(d)i^2\
&=i^2sum_{d|i}varphi(d)\
&=i^2 imes i\
&=i^3
end{aligned}
]
接着套用公式
[egin{aligned}
S(n)&=frac{sum_{i=1}^n(f*g)(i)-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{sum_{i=1}^ni^3-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{frac{n^2(n+1)^2}{4}-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{g(1)}\
&=frac{frac{n^2(n+1)^2}{4}-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)}{1}\
&=frac{n^2(n+1)^2}{4}-sum_{d=2}^ng(d)S(left lfloorfrac{n}{d}
ight
floor)
end{aligned}
]
(sum g(d))的求法跟上面一样,接着还是套用模板去做就行了
例题
模板题,其实就是前两个推导,就不写题解了
选数,题解
神犇和蒟蒻,题解
如果你懂了杜教筛核心思想,做题基本上就差不多了
本质上是这个递推函数的变形,剩下的问题主要就是 (g(n)) 的构造和 (f*g) 的推导,数学足够强的话应该也不构成问题
因为博主太菜了,或许不会再更新其他两个筛法的博客了
例题可能会看心情实时更新,但博主是个老鸽子了,毕竟连游记都懒得写
——后记