不学莫反,不学狄卷,就不能叫学过数论
事实上大概也不是没学过吧,其实上赛季头一个月我就在学这东西,然鹅当时感觉没学透,连杜教筛复杂度都不会证明,所以现在只好重新来学一遍了(/wq
真·实现了水平的负增长(((
1. (mu) 与 (varphi)
真就从头开始呗
对于整数 (n=p_1^{alpha_1} imes p_2^{alpha_2} imescdots imes p_k^{alpha_k}),定义莫比乌斯函数 (mu(n)) 为:
也就是说如果 (n) 含平方因子则 (mu(n)=0),否则 (mu(n)) 等于 ((-1)^{omega(n)}),其中 (omega(n)) 表示 (n) 中不同质因子的个数。
关于莫比乌斯函数,最重要的一条是 (sumlimits_{d|n}mu(d)=[n=1]),证明就假设 (n) 中恰好含有 (k) 个质因子 (p_1,p_2,cdots,p_k),那么如果 (k=0) 显然 (n=1),( ext{LHS}=1),等式成立。否则 ( ext{LHS}) 的组合意义就是从 (n) 个的 (k) 个质因子中选出 (x) 个并对结果产生 ((-1)^x),根据我们幼儿园就知道的结论 (sumlimits_{i=0}^kdbinom{k}{i}(-1)^i=[k=0]) 可知 ( ext{LHS}=0)
在下面数论函数的部分中我们将深入讨论这个问题。
同样对于 (ninmathbb{N}_+),我们定义欧拉函数 (varphi(n)=sumlimits_{i=1}^n[iperp n]),其中 ([xperp y]) 表示 (x,y) 是否互质,那么对于 (n=p_1^{alpha_1}·p_2^{alpha_2}·cdots·p_k^{alpha_k}) 有 (varphi(n)=n·prodlimits_{i=1}^kdfrac{p_i-1}{p_i})
欧拉函数同样有一个非常重要的性质 (sumlimits_{dmid n}varphi(d)=n),比较感性的证明是将所有分子 (le n),分母 (=n) 的分数排成一列并约分,那么一个约分完后的分数与一个满足 (xperp y,ymid n) 的二元组 ((x,y)) 形成双射。而满足 (xperp y,ymid n) 的二元组 ((x,y)) 个数就是 (sumlimits_{dmid n}varphi(d)=n),得证。
2. 数论函数与积性函数
我们称一个定义在 (mathbb{N}_+) 上的函数 (f) 为数论函数,那么显然数论中常用的 (mu(x),varphi(x)),以及约数个数函数 (d(x))、约数和函数 (sigma(x)) 都是数论函数,而对于一个数论函数而言,如果 (forall x,y, ext{s.t.}xperp y),都有 (f(xy)=f(x)f(y)),我们就称该数论函数为积性函数,特别地,如果 (forall x,y) 均有 (f(xy)=f(x)f(y)),那么我们就称该函数为完全积性函数,显然完全积性函数 (in) 积性函数。
对于积性函数 (f) 而言,根据定义 (f(1)=f(1)f(1)),因此必须有 (f(1)=1)。而对于整数 (n=p_1^{alpha_1}·p_2^{alpha_2}·cdots·p_k^{alpha_k}),根据积性函数的定义 (f(n)=prodlimits_{i=1}^kf(p_i^{alpha_i})),因此我们只需求出函数 (f) 在质数次幂处的取值即可确定整个积性函数,同理对于积性函数我们只需求出其在质数处的取值即可确定整个积性函数。
抛一亿些常用的积性函数:
- (varphi(x)),即欧拉函数,是积性函数但不是完全积性函数,在前文中已经提及。
- (mu(x)),即莫比乌斯函数,是积性函数但不是完全积性函数,在前文中已经提及。
- (epsilon(x)),元函数,满足 (epsilon(x)=[x=1]),也就是说 (epsilon(x)) 只有在 (1) 处函数值为 (1),其余位置均为 (0),完全积性函数,至于为什么叫“元”函数将在下文中讲解
- (I(x)),恒等函数,不论你带什么值进去都会返回 (1),完全积性函数
- ( ext{id}(x)),单位函数,( ext{id}(x)=x),完全积性函数
- ( ext{id}_k(x)),幂函数,( ext{id}(x)=x^k),完全积性函数,单位函数与恒等函数的更一般情形
- (d(x)),约数个数函数,(d(x)=sumlimits_{dmid x}1),是积性函数但不是完全积性函数
- (sigma(x)),约数个数和函数,(sigma(x)=sumlimits_{dmid x}d),是积性函数但不是完全积性函数
- (sigma_k(x)),除数函数,(sigma_k(x)=sumlimits_{dmid x}d^k),是积性函数但不是完全积性函数,约数个数函数与约数个数和函数的更一般情形。
3. 狄利克雷卷积
对于两个数论函数 (f,g),我们定义以下两种运算,其运算结果均为数论函数:
- 点乘:((f·g)(i)=f(i)g(i))
- 狄利克雷卷积:((f*g)(i)=sumlimits_{dmid i}f(d)g(dfrac{i}{d}))
那么对于两个积性函数 (f,g) 而言,有它们的点乘、狄利克雷卷积均为积性函数,对于完全积性函数而言得到的结果也就自然是完全积性函数了(当然有一个例外:完全积性函数与完全积性函数的狄卷不一定是完全积性函数,例如 (I*I=d) 就不是完全积性函数),这里稍微证明一下:
- 点乘:假设 (f·g=h),那么显然 (h(xy)=f(xy)g(xy)=f(x)g(x)f(y)g(y)=h(x)h(y)(xperp y))
- 狄利克雷卷积:假设 (f*g=h),那么 (h(xy)=sumlimits_{dmid xy}f(d)g(dfrac{xy}{d})(xperp y)),而由于 (xperp y) 且 (dmid x,dmid y),必然有 (gcd(x,d)gcd(y,d)=d)(因为 (d) 每个质因子要么出现在 (x) 中要么出现在 (y) 中,且次数不超过对应的数的质因子的次数),又显然 (gcd(gcd(x,d),gcd(y,d))=1),因此 (f(d)=f(gcd(x,d))f(gcd(y,d))),同理 (g(dfrac{xy}{d})=g(gcd(x,dfrac{xy}{d}))g(gcd(y,dfrac{xy}{d}))),又因为 (d) 和 (dfrac{xy}{d}) 合起来包揽了 (x) 的全部质因子的全部次数,因此 (gcd(x,dfrac{xy}{d})gcd(x,d)=x),(gcd(y,dfrac{xy}{d})gcd(y,d)=y),故 (h(xy)) 可以改写作 (sumlimits_{d_xmid x}f(d_x)g(dfrac{x}{d_x})sumlimits_{d_ymid y}f(d_y)g(dfrac{y}{d_y})=h(x)h(y)),符合积性函数的定义。
当然,在点乘和狄卷中,必然是狄卷的重要性更高一些,因为不难看出上面不少积性函数都可以写成两个积性函数的狄卷。因此下面将着重讲解狄卷的一些性质,容易发现对于任意数论函数 (f,g,h),均有:
- 交换律:(f*g=g*f)
- 结合律:((f*g)*h=f*(g*h))
- 对加法的分配律:((f+g)*h=f*h+g*h)
- 单位元:(f*epsilon=f)(所以 (epsilon) 也被称为元函数)。注意,((f*I)(n)=sumlimits_{dmid n}f(d)),因此 (I) 不是狄卷单位元,但 (f·I=f),因此 (I) 是点乘单位元。
- 逆元:对于所有 (f(1)
e 0) 的数论函数 (f) 均 (exists g) 满足 (f*g=epsilon),具体构造就像多项式求逆一样按位计算呗,此时我们记 (g=f^{-1},f=g^{-1}),记作 (f)((g))的逆。有一个性质是任意积性函数的逆仍是积性函数,证明可见 ycx 神仙的 blog,密码是
********
(大雾
以上几条读者自证不(最后一条的最后一点大概是“有亿点”?)难,同时也说明所有第 (1) 项非零的数论函数与狄卷运算构成一个 Abel 群(((
显然不少有关数论函数的语言都可以用狄卷来描述,这里列举了几条:
- (varphi*I= ext{id}),即 (sumlimits_{dmid n}varphi(d)=n)
- (mu*I=epsilon),即 (sumlimits_{dmid n}mu(d)=[n=1]),上式同时也说明了 (mu=I^{-1}),因此下次看到什么式子卷上 (I) 也可以在左右两边都卷上一个 (mu)。
- (d=I*I,sigma= ext{id}*I,sigma_k= ext{id}_k*I),因此下次看到 (d,sigma,sigma_k) 这类我们相对来说不那么熟悉的积性函数可以将其写成我们熟悉的 (I,mu, ext{id}) 之类的积性函数。
- 对于任意数论函数 (f,g) 和完全积性函数 (h) 均有 ((f*g)·h=(f·h)*(g·h)),此式子又被称为提公因式(注意中间是点积!!!11)
- 对于任意完全积性函数 (f),一定有 (f·mu=f^{-1}),这是因为 (f*(f·mu)=(f·I)*(f·mu)=f·(mu*I)=epsilon)。
还有一个比较有意思,同时也可以用狄卷来推的东西是 (dfrac{varphi(n)}{n}=sumlimits_{dmid n}dfrac{mu(d)}{d}),证明的话就 (varphi*I= ext{id}),左右两边都卷上 (mu) 可以得到 ( ext{id}*mu=varphi),也即 (varphi(n)=sumlimits_{dmid n}mu(d)dfrac{n}{d}),左右两边同除 (n) 即可得到上式。
4. 莫比乌斯反演
通常情况而言莫反的形式有三种:
-
([n=1]=sumlimits_{dmid n}mu(d)),因此下次在题目中看到类似于 ([gcd(x,y)=1]) 的柿子就可以将其化成 (sumlimits_{dmid x,dmid y}mu(d))(其实和集合反演有点像,或者说,集合反演和莫比乌斯反演有点像,因为集合反演在知识点树上的顺序严格后于莫比乌斯反演)
-
(F_i=sumlimits_{dmid i}f_dLeftrightarrow f_i=sumlimits_{dmid i}mu(dfrac{i}{d})F_d),证明的话大概就 (F=I*fLeftrightarrow f=F imesmu)
-
(F_i=sumlimits_{imid d}f_dLeftrightarrow f_i=sumlimits_{imid d}mu(dfrac{d}{i})F_d),这种形式的莫比乌斯反演无法直接用狄卷的形式描述,因此我们采取另一种方式——暴力带:
[egin{aligned} ext{RHS}&=sumlimits_{imid d}mu(dfrac{d}{i})sumlimits_{dmid t}f(t)\ &=sumlimits_{imid t}f(t)sumlimits_{(d/i)mid(t/i)}mu(dfrac{d}{i})\ &=sumlimits_{imid t}f(t)epsilon(dfrac{t}{i})\ &=f(i) end{aligned} ]学过二项式反演的同学应该会觉得这玩意儿的证明过程与二项式反演的证明过程有异曲同工之妙。
在实际解题中碰到莫反的题一般都是反复套用这几个与莫反有关的公式,然后再套用整除分块/各种筛子将复杂度变为线性/(sqrt{n})/(sqrt{n}log n)/(n^{2/3}) 这类复杂度,这里稍微总结几个技巧:
-
看到式子中的 (gcd),一般可以枚举 (gcd=d) 然后把后面的东西贡献的系数加一个 ([gcd(i,j)=d]),然后再通过枚举 (dfrac{i}{d}) 和 (dfrac{j}{d}) 把艾弗森括号里的东西进一步转化为 ([gcd(i,j)=1]),然后再用莫比乌斯反演转化为 (sumlimits_{pmid i,pmid j}mu(p)),然后交换 (Sigma) 把 (p) 放到第一个枚举的即可。
-
如果看到式子中带有只与 (lfloordfrac{n}{dp} floor) 有关的式子,通常我们的想法是枚举 (T=dp),然后对 (T) 整除分块,那么后面只与 (lfloordfrac{n}{T} floor) 有关的式子就可以线性预处理(总复杂度 (sqrt{n}))/再套一个整除分块(总复杂度 (n^{3/4}))了,而与 (d,p) 分别有关的式子写成 (T) 的函数 (f(T)) 一般是两个积性函数的狄卷,一般也就可以线性筛了。
-
看到先枚举 (i) 再枚举 (jmid i) 的求和/积式我们可以考虑交换求和/积的顺序,先枚举 (j) 再枚举 (i),这样会出现下取整,就可以整除分块了。
好,既然你已经知道了这么多东西,就让我们来练习一些题目吧:
upd on 2021.8.7:
呐,鸽子 tzc 终于来补了,奇迹奇迹(
1. P2257 YY的GCD
基础题,上赛季第一天就 AC 了(
枚举 gcd 反演即可,u1s1 我对约数和倍数的反演不太熟练,我一般比较喜欢 ([gcd(i,j)=1]) 这样的反演方式,反正都是殊途同归。
整除分块走起,后面那东西枚举 (p) 可以 (dfrac{10^7}{p}) 预处理,时间复杂度 (mathcal O(Tsqrt{n}+nloglog n))
2. P4449 于神之怒加强版
Yet another 板板题。
依然整除分块走起,后面那东西调和级数枚举实测能过,不过注意到后面的函数 (f(T)) 可以写成 ( ext{id}_k*mu) 的形式,因此可以考虑线筛,复杂度大概可以达到 (mathcal O(n+Tsqrt{n}))。
3. P4450 双亲数
我为什么要刷这样的题
直接上下界全除以 (d),然后枚举 gcd 反演即可。
4. P4318 完全平方数
首先一眼二分答案,问题转化为求 ([1,n]) 中有多少个数不含平方因子。不难发现一个数 (x) 不含平方因子等价于 (mu^2(x) e 0),问题可进一步转化为 (sumlimits_{i=1}^nmu^2(i))。
第一眼感觉要什么奇怪的筛法,但冷静思考一下发现不用,我们考虑将每个含平方因子的数一一筛去,即枚举每个含平方因子的数的平方因子 (i^2),那么共有 (lfloordfrac{n}{i^2} floor) 个含平方因子 (i) 的数,但这样会算重,譬如对于 (x=900),它在 (i=2,i=3,i=5) 时各会被算一次,因此考虑容斥,不难发现 (2^2,3^2) 均为 (x) 的平方质因子等价于 ((2 imes 3)^2) 为 (x) 的平方质因子,那么我们再将答案减去 (i=6,i=10,i=15) 的情况,但这样 (i=30) 的情况又会被多减一次。不难发现上面每个数被加的次数就是 (mu(i)),因此我们考虑直接枚举这里的 (i),那么我们能得到 (sumlimits_{i=1}^nmu^2(i)=sumlimits_{i=1}^{sqrt{n}}lfloordfrac{n}{i^2} floormu(i)),这样可以在 (mathcal O(sqrt{n})) 的时间内计算该前缀和。这东西大概算是个定式(?),记住就好了(
5. P6810 「MCOI-02」Convex Hull 凸包
还是道套路题啊……
然后我们惊奇地发现 (d*mu=I*I*mu=I*epsilon=I),于是
后两个都是狄利克雷后缀和的形式,可以 (mathcal O(nloglog n)) 求出。
6. P3172 [CQOI2015]选数
这道题还算有意思(
首先由于 (gcd) 必须为 (k) 这个限制的存在,我们所选出来的数必须都是 (k) 的倍数,因此我们可以考虑将 (L,R) 分别变为 (lceildfrac{L}{k} ceil,lfloordfrac{R}{k} floor),这样限制即可转化为选出的数的 (gcd) 必须为 (1)。
还是考虑反演
到这里,大部分人都是对 (i) 整除分块+杜筛求 (mu) 的前缀和,这里提供一个不太一样的解法,我们考虑对 (iin[1,R-L+1]) 暴力地求解上面式子,对于 (i>R-L+1),如果 (lfloordfrac{R}{i} floor-lfloordfrac{L-1}{i} floor e 0),必然有 (lfloordfrac{R}{i} floor-lfloordfrac{L-1}{i} floor=1),也就是说 (i>R-L+1) 的贡献就是所有 ([L,R]) 中的数的因子的并与 ([R-L+2,infty)) 交中所有数的 (mu) 之和,那么怎么求这个值呢,考虑 (xin[L,R]),有 (sumlimits_{dmid x}mu(d)=epsilon(x)),我们扣掉 (dle R-L+1) 的部分,可以得到 (epsilon(x)-sumlimits_{dmid x,dle R-L+1}mu(d)=sumlimits_{d>R-L+1,dmid x}mu(d)),而根据“对于 (>R-L+1) 的因子,每个因子最多是 ([L,R]) 中一个数的约数”这个性质,(>R-L+1) 的因子的贡献就是每个 (x) 等式右边的值相加,因此我们可以拿 (sumlimits_{i=L}^Repsilon(i)) 减去 (sumlimits_{x=L}^Rsumlimits_{dmid x,dle R-L+1}mu(d)) 算出右式的值,前者简单特判即可,后者每枚举一个 (dle R-L+1) 都调和级数地累加下贡献即可,这样最后复杂度就是 (mathcal O((R-L+1)log(R-L+1)))
7. P6222 「P6156 简单题」加强版
按照套路颓式子:
不难发现后两个 (sum) 中的内容可以写为关于 (lfloordfrac{n}{td} floor) 的函数 (f(lfloordfrac{n}{td} floor)),那么我们可以把它预处理出来(具体预处理方法就是观察每个 (i+j) 被累加入贡献了多少次,然后维护 ( ext{id}_k) 的前缀和转移),那么式子可以化为
老套路,枚举 (T=td),那么
其中 (g(T)=sumlimits_{td=T}mu(t)t^kmu^2(d)d^{k+1}),显然 (g=(mu· ext{id}_k)*(mu^2· ext{id}_{k+1})) 是积性函数,可以线筛。
这题好卡空间……卡空间的方法就是不要预处理 (mu) 直接处理 (g),不难发现如果 (x) 存在一个质因子次数 (ge 3) 那么 (g(x)=0),对于 (x) 中某个次数为 (2) 的质因子 (p),贡献为 (-p^{2k+1}),对于某个次数为 (1) 的质因子 (p),贡献为 (p^{k+1}-p^k),那么在我们线筛过程中,如果 (imod p_j
e 0),那么 (i imes p_j) 中 (p_j) 的次数显然为 (1),否则我们可以根据 (dfrac{i}{p_j}mod p_j) 是否为 (0) 来判断 (i imes p_j) 中 (p_j) 的次数是否 (ge 3)。还有就是标记数组开成 bitset
,质数数组缩小 (10)((ln n))倍等。
8. BZOJ 2226 LCMsum
套路题……推式子部分直接不说了,放一下最终结果:
还有一个注意点,就是求所有数的因子时,千万不要用 vector
存,这样常数特别特别大,(10^6) (nln n) 需要跑 1s……
似乎有一个比这道题不知道强多少倍的版本,就是把此题输入 (n) 时输出的值看作一个函数 (f(n)),要你求 (f) 函数的前缀和,数据范围 (10^{10}),不知道会不会做.jpg
5. 狄利克雷前/后缀和
一个比较 trivial 的小 trick 吧……不过能在某种程度上优化复杂度(
大概就是如果给定序列 (a),需要我们求出序列 (b) 满足 (b_n=sumlimits_{dmid n}a_d)(或者 (b_n=sumlimits_{nmid d}a_d),这里以前一种情况为例),那么我们显然可以想到一个调和级数的做法,具体来说就是枚举每个 (d) 以及所有 (dmid n) 然后将贡献累加一下即可,复杂度 (nln n),不过这样的复杂度还不够优秀,注意到如果我们把每个数的质因数分解形式表示成 (n=p_1^{alpha_1} imes p_2^{alpha_2} imescdots imes p_k^{alpha_k}),那么 (n=p_1^{alpha_1} imes p_2^{alpha_2} imescdots imes p_k^{alpha_k}) 可以贡献到 (d=p_1^{eta_1} imes p_2^{eta_2} imescdots imes p_k^{eta_k}) 当且仅当 (forall k,eta_klealpha_k),因此如果我们把每个质因子看作一维,那么这东西就是个高维前缀和,高维前缀和扫一遍即可,复杂度同埃氏筛,(nloglog n)。
模板题代码:
const int MAXN=2e7;
int n,pr[MAXN/10+5],pcnt=0;u32 seed,ans,a[MAXN+5];
bitset<MAXN+5> vis;
void sieve(int n){
for(int i=2;i<=n;i++){
if(!vis[i]) pr[++pcnt]=i;
for(int j=1;j<=pcnt&&pr[j]*i<=n;j++){
vis[pr[j]*i]=1;if(i%pr[j]==0) break;
}
}
}
inline u32 rng60(){seed^=seed<<13;seed^=seed>>17;seed^=seed<<5;return seed;}
int main(){
scanf("%d%u",&n,&seed);sieve(n);
for(int i=1;i<=n;i++) a[i]=rng60();
for(int i=1;i<=pcnt;i++) for(int j=1;j*pr[i]<=n;j++)
a[j*pr[i]]+=a[j];
for(int i=1;i<=n;i++) ans^=a[i];
printf("%u
",ans);
return 0;
}
6. 数论函数(一般是积性函数)的三种筛法
线性筛
最基础的筛法。回顾一下埃氏筛的过程,我们每找到一个质数都要遍历一遍所有它的倍数并将它筛去,我们发现这样筛会导致不少合数被筛去了多次,因此我们考虑加一个小小的优化:我们不再只枚举质数的倍数并将这些倍数全部筛去,而是对于每个数 (i),我们枚举其质数倍 (ip),并将 (ip) 筛去。该筛法的核心就在于,如果我们发现 (pmid i),那么筛完 (ip) 之后就直接 break
,不难发现这样每个合数只会在其最小质因子处被筛去,因此复杂度是严格线性的。
由于每次筛 (ip) 时候都有 (p) 为 (ip) 的最小质因子,并且若 (p mid i),必然有 (pperp i),因此几乎所有积性函数,譬如 (varphi,mu,d,sigma,sigma_k) 都可以通过线性筛求出,包括幂函数 ( ext{id}_k),暴力对每个数进行快速幂是 (mathcal O(nlog k)) 的,不过如果我们只对质数暴力快速幂,然后非质数的部分利用完全积性函数的性质写成两个函数的乘积,那么复杂度即可实现 (mathcal O(dfrac{n}{ln n}·log k)=mathcal O(n))。
事实上对于任意积性函数,如果其在质数的幂处的函数值 (f(p^k)) 可以 (mathcal O(1)) 算得,那么该函数就可以在线性时间内进行线性筛,具体步骤就是先通过一遍线筛筛出质数,以及每个数最小质因子在该数质因数分解中的幂 (mx_i)——譬如 (mx_{24}=2^3=8),具体步骤就是,显然在线筛过程中我们遍历到的 (p) 都是 (ip) 的最小质因子,那么若 (p mid i),(p) 在 (ip) 质因数分解形式中的次数显然为 (1),那么 (mx_{ip}=p),否则 (p) 在 (ip) 质因数分解形式中的次数即是 (p) 在 (i) 质因数分解形式中的次数 (+1),故 (mx_{ip}=mx_i·p),预处理出 (mx) 之后显然就有 (f(i)=f(dfrac{i}{mx_i})·f(mx_i)),然后就可以线性地计算了。
杜教筛
我们假设我们已知一个狄利克雷卷积式 (f*g=h),我们先假设我们已经有惊人的能力求出 (g,h) 的前缀和,考虑怎样求出 (f) 的前缀和 (sumlimits_{i=1}^nf(i))。首先我们先把 (h) 的前缀和写出来:
如果我们记 (s_f(n)=sumlimits_{i=1}^nf(i)),那么考虑在上式中咱们考虑把 (j=1) 这一项单独提一项出来,那么剩余部分则可以写成:
然后我们惊奇地发现后面那东西可以整除分块,于是我们考虑这样一个做法:每次整除分块并递归地求 (lfloordfrac{n}{i}
floor) 处的前缀和,并记忆化搜索已经求过值的部分,可以证明这样复杂度是 (n^{0.75}) 的,因为:
- (lfloordfrac{lfloorfrac{n}{a} floor}{b} floor=lfloordfrac{n}{ab} floor)
- 在我们整除分块的交界界处 (r) 和 (r+1),必然有 (lfloordfrac{n}{lfloorfrac{n}{r} floor} floor=r),也就是说整除分块时每一块的右边界都可以看作某个 (lfloordfrac{n}{k} floor)。
上面两条性质说人话就是我们每次记忆化搜索时用到的 (f(i)) 都可以写成 (lfloordfrac{n}{k} floor) 的形式,再加上每次整除分块的 (sqrt{i}),总复杂度就是 (sumlimits_{x,exists k,s.t.lfloorfrac{n}{k} floor=x}sqrt{x}),这东西可以分两部分考虑:
-
(xlesqrt{n}),那么显然所有 (x) 都符合条件,对和式的贡献即为 (sumlimits_{xlesqrt{n}}sqrt{x}),根据微积分
[int_0^{sqrt{n}}sqrt{x}\,mathrm dx=dfrac{2}{3}n^{0.75} ]可知该和式也是 (n^{0.75}) 级别的。
-
(x>sqrt{n}),那么对应的 (klesqrt{n}),对和式的贡献即为 (sumlimits_{klesqrt{n}}sqrt{dfrac{n}{k}}),根据微积分:
[int_0^{sqrt{n}}sqrt{dfrac{1}{x}}\,mathrm dx=2x^{0.25} ]可知 (sumlimits_{klesqrt{n}}sqrt{dfrac{n}{k}}=mathcal O(sqrt{n}·n^{0.25})=mathcal O(n^{0.75}))。
因此总复杂度也是 (n^{0.75}) 级别的。这个复杂度还不够优秀,我们考虑设一个阈值 (T),对 (le T) 的部分线筛,(>T) 的部分再杜教筛,那么复杂度变为 (mathcal O(T+sumlimits_{i=1}^{lfloorfrac{n}{T} floor}sqrt{dfrac{n}{i}})=mathcal O(T+sqrt{n}·sqrt{dfrac{n}{T}})),显然取 (T=n^{2/3}) 时候复杂度达到最小值 (n^{2/3})。
实测大约 ( ext{TL}=1s) 时候杜教筛能跑 (n=10^{10})。
模板题代码:
const int MAXN=1e7;
int pr[MAXN/10+5],prcnt=0,vis[MAXN+5];
ll phi[MAXN+5],mu[MAXN+5];
void sieve(int n){
phi[1]=mu[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]) pr[++prcnt]=i,phi[i]=i-1,mu[i]=-1;
for(int j=1;j<=prcnt&&pr[j]*i<=n;j++){
vis[pr[j]*i]=1;
if(i%pr[j]==0){phi[i*pr[j]]=phi[i]*pr[j];break;}
phi[i*pr[j]]=phi[i]*phi[pr[j]];mu[i*pr[j]]=-mu[i];
}
}
for(int i=2;i<=n;i++) phi[i]+=phi[i-1],mu[i]+=mu[i-1];
}
unordered_map<int,ll> sphi,smu;
ll sum_phi(int n){
if(n<=MAXN) return phi[n];
if(sphi.count(n)) return sphi[n];
ll sum=1ll*n*(1ll+n)/2;
for(ll l=2,r;l<=n;l=r+1){
r=n/(n/l);
sum-=sum_phi(n/l)*(r-l+1);
} return sphi[n]=sum;
}
ll sum_mu(int n){
if(n<=MAXN) return mu[n];
if(smu.count(n)) return smu[n];
ll sum=1;
for(ll l=2,r;l<=n;l=r+1){
r=n/(n/l);
sum-=sum_mu(n/l)*(r-l+1);
} return smu[n]=sum;
}
int main(){
sieve(MAXN);int qu;scanf("%d",&qu);
while(qu--){int n;scanf("%d",&n);printf("%lld %lld
",sum_phi(n),sum_mu(n));}
return 0;
}
那么怎么求出 (g,h) 的前缀和呢?如果 (g,h) 是我们非常熟悉的函数,譬如恒等函数、元函数、幂函数,我们能够在 (mathcal O(1)/mathcal O(log n)) 的时间内求出其前缀和当然最好(注意,由于复杂度瓶颈在于整除分块的根号,因此即便是这里的 (log n) 不影响复杂度的 (n^{2/3}),如果硬要说的话那我们只会对 (>T) 的部分求一遍前缀和,(le T) 的部分已经线筛出来了直接调用预处理出的值即可,因此求前缀和的次数是 (n^{1/3}) 级别的,故复杂度可以视作 (n^{2/3}+n^{1/3}log n)),那如果 (g,h) 不好直接求怎么办呢?不难发现在整除分块的过程中,我们需要对 (g,h) 求前缀和的位置全部可以写成 (lfloordfrac{n}{k} floor) 的形式,因此如果 (g,h) 可以杜教筛处理,且 (f) 可以线筛处理,那么 (f) 也可以杜教筛在 (mathcal O(n^{2/3})) 的时间内求出前缀和。还有一个小问题,如果我们要求的是 (h) 怎么办呢?哈哈哈哈,如果这你都不能及时反应过来那你可能要想想自己的状态了,我们求 (f) 的前缀和时不就是从 (h) 的前缀和引入的吗?现在你把这个过程倒过来,整除分块求一下等号右边的东西不就行了吗(),按照之前的推论,如果 (g,h) 可以杜教筛求出,那 (f) 也可以杜教筛求出。
最后列一些常见函数的杜教筛筛法:
- (mu*I=epsilon)
- (varphi*I= ext{id})
- (sigma_k=I* ext{id}_k)
- (mu· ext{id}_k),按照提公因式卷上一个 (I·id_k) 然后把它提出来变成 ((mu*I)·id_k=epsilon),即 ((mu· ext{id}_k)* ext{id}_k=epsilon)
- (varphi· ext{id}_k),同上,变成 ((varphi*I)· ext{id}_k= ext{id}_{k+1})
- (mu^2*(mu· ext{id})),卷上一个 ( ext{id}) 变成 (mu^2*(mu· ext{id})*(I· ext{id})=mu^2*((mu*I)· ext{id})=mu^2),至于 (mu^2) 的前缀和怎么求……见上面的例题 P4318.
- ……………………
Min_25 筛
这估计是这三个筛法中最难写的一个吧……
Min_25 筛可以适用于求以下数论函数 (f) 的前缀和:
- (f(p^k)) 是关于 (p) 的低阶多项式,一般次数不超过 (3)(否则常数可能上天了)
- (f) 是积性函数。
首先我们考虑将 ([1,n]capmathbb{Z}) 分成质数和合数两类,(1) 的贡献显然为 (1) 最后单独加上即可,也就是设 (mathbb{PRIME}) 表示质数组成的集合,那么
考虑对这两部分分开来求和,首先是质数的部分,注意到 ([1,n]) 的质数可能非常多,不过对于一个合数 (x) 而言,其最小质因子一定不会超过 (sqrt{n}),因此我们考虑这样一个思想:我们筛出 ([1,sqrt{n}]) 以内的质数,然后用这些质数一一去筛那些目前还没有被筛过的合数,然后拿目前的总贡献减去这些被筛掉的数的贡献,然后筛到最后剩余的贡献就是答案。由于任意一个多项式不一定是完全积性函数,因此我们可以将多项式拆成常数项、一次项、二次项……(其实在大部分题目中这里并没有省略号/cy)分别处理,因此这个问题就转化为这样一个问题:
考虑按照上面的思想设计一个 (dp):(g(n,i)) 表示在 ([2,n]) 中,用前 (i) 个质数筛去后,未被筛去的数的 (k) 次方之和,考虑转移,从 (i-1 o i),显然会筛去最小质因子刚好 (=p_i) 的那些合数,考虑怎样求这些数的贡献,我们枚举这样的合数 (x) 除以 (p_i) 的值,显然 (dfrac{x}{p_i}lelfloordfrac{n}{p_i} floor),那么必须有 (dfrac{x}{p_i}) 的最小质因子 (ge p_i),那么这样的 (dfrac{x}{p_i}) 必然是没有被前 (i-1) 个质数筛去的,但在 (lelfloordfrac{n}{p_i} floor) 的没有被前 (i-1) 个质数筛去的数中,还有一部分是 (<p_i) 的质数,因此我们要扣掉它们的贡献,根据完全积性函数的性质,我们求出这些 (dfrac{x}{p_i}) 们的 (k) 次方和后,乘上 (p_i^k) 就是所有最小质因子 (=p_i) 的合数的 (k) 次方和,也就是我们应该扣掉的贡献,因此我们可以得到:
直接对每个 (n) 算一遍显然爆掉,不过还是仿照杜教筛的复杂度分析,我们要求的部分肯定是 (n) 的关键点,也就是可以被写成 (lfloordfrac{n}{k} floor) 的点,因此我们考虑将这部分的 (2sqrt{n}) 个点重新编个号,编号的方法大概就先枚举一遍 ([1,sqrt{n}]) 然后依次编号 (1,2,3,cdots,sqrt{n}),然后倒序枚举 (i=sqrt{n} o 1),如果 (lfloordfrac{n}{i} floor elfloorsqrt{n} floor) 那么就给它一个新的编号,然后 (x) 查询编号时如果 (le sqrt{n}) 就直接返回 (x) 本身,否则返回 (lfloordfrac{n}{x} floor) 的编号,这样即可实现将关键点从小到大编号。然后对这些点做一遍 DP 即可,由于第二维的转移只涉及到 (i-1),因此可以滚动数组优化,具体方案就是每次正序枚举第二维时倒序枚举第一维。
初始值 (g(n,0)=sumlimits_{i=2}^ni^k),可以插值/直接套公式求出,注意,这里的下界是 (2),因此套公式求初始值时需要扣掉 (1) 的贡献,否则会 WA!!!!!1111
接下来考虑怎么求和。根据前面的性质,每个合数的最小质因子都 (lesqrt{n}),因此我们考虑再设一个 (S(x,y)) 表示在 ([2,x]) 中,所有最小质因子 (>p_y) 的数的贡献和,注意,由于 (1) 比较特殊,因此我们暂且不要把它设计入状态中,最后答案加上 (f(1)=1) 即可。那么这样的贡献可以分为两部分,质数和合数,对于质数,显然就是 ((p_y,x]) 中所有质数的 (f) 值之和,而由于我们需要用到的 (x) 都可以表示为 (lfloordfrac{n}{k} floor) 的形式,因此我们直接拿第一部分预处理出的前缀和相减即可,对于合数,我们暴力的枚举其最小质因子 (p_i) 以及最小质因子的次数 (p_i^j),那么对于一个满足条件的合数 (v) 而言,必然有 (dfrac{v}{p_i^j}) 的最小质因子 (>p_i),因此这部分贡献就是 (S(lfloordfrac{x}{p_i^j} floor,i)),又根据积性函数的性质 (f(v)=f(dfrac{v}{p_i^j})f(p_i^j)),因此总贡献就是 (f(p_i^j)(S(lfloordfrac{x}{p_i^j} floor,i))),不过注意,由于 (1) 没有设入状态,因此如果 (p_i^j) 也是合数,即 (j>1),还需将 (p_i^j) 的状态加入答案,因此总贡献实际上应该是 (f(p_i^j)(S(lfloordfrac{x}{p_i^j} floor,i)+[j>1]))。因此我们有:
递归求解即可,边界条件 (xle p_y) 时返回 (0)。根据某个我不知道的定理,这里不需要记忆化搜索复杂度也能得到保证。
注意点:注意,在枚举 (i) 时候必须有 (p_i^2le x),否则会 WA。
最后稍微证一下复杂度,不难发现前后两部分复杂度是一样的,因此考虑前面一部分,显然对于一个关键点 (x),有 (pi(sqrt{x})) 个质数 (p_i) 满足筛 (p_i) 时会枚举到 (x),那么 Min_25 筛的复杂度就可以写作
而由于对于函数 (f(x)=dfrac{x}{ln x}),(f'(x)=dfrac{ln x-1}{ln^2x}) 在 ((1,infty)) 上严格大于 (0),因此 (f(x)) 在 ((1,infty)) 上为增函数,故前一个 (sum) 严格小于后一个 (sum),因此复杂度又可写作:
到这里不太好直接积,因此我们考虑玄学一波,由于 (lnsqrt{n/i}) 与 (log n) 仅仅只是常数的区别,因此我们将分母放成 (log n),于是
根据著名积分:
可知
显然 (sum) 里的东西等于 (dfrac{n^{0.25}}{log n}),因此 Min_25
筛的复杂度为 (mathcal O(dfrac{n^{0.75}}{log n}))
模板题代码:
const int SQRT=1e5;
const int MOD=1e9+7;
const int INV2=5e8+4;
const int INV6=166666668;
int pr[SQRT/7+5],prcnt=0,vis[SQRT+5];
int sum1[SQRT/7+5],sum2[SQRT/7+5];
void sieve(int n){
for(int i=2;i<=n;i++){
if(!vis[i]) pr[++prcnt]=i;
for(int j=1;j<=prcnt&&pr[j]*i<=n;j++){
vis[pr[j]*i]=1;if(i%pr[j]==0) break;
}
}
for(int i=1;i<=prcnt;i++){
sum1[i]=(sum1[i-1]+pr[i])%MOD;
sum2[i]=(sum2[i-1]+1ll*pr[i]*pr[i])%MOD;
}
}
ll n,pos[SQRT*2+5];int lim,pos_n=0;
int id[SQRT+5],s1[2*SQRT+5],s2[2*SQRT+5];
int getid(ll x){return (x<=lim)?x:id[n/x];}
void init(){
for(int i=1;i<=lim;i++) pos[++pos_n]=i;
for(int i=lim;i;i--) if(n/i>lim) pos[id[i]=++pos_n]=n/i;
for(int i=1;i<=pos_n;i++){
s1[i]=1ll*((pos[i]+1)%MOD)*(pos[i]%MOD)%MOD*INV2%MOD-1;
s2[i]=1ll*((pos[i]+1)%MOD)*((pos[i]<<1|1)%MOD)%MOD*(pos[i]%MOD)%MOD*INV6%MOD-1;
}
for(int i=1;i<=prcnt;i++) for(int j=pos_n;j&&1ll*pr[i]*pr[i]<=pos[j];j--){
s1[j]=(s1[j]-1ll*pr[i]*(s1[getid(pos[j]/pr[i])]-sum1[i-1]+MOD)%MOD+MOD)%MOD;
s2[j]=(s2[j]-1ll*pr[i]*pr[i]%MOD*(s2[getid(pos[j]/pr[i])]-sum2[i-1]+MOD)%MOD+MOD)%MOD;
}
}
int calc(ll x,int y){
if(pr[y]>=x) return 0;
int sum=(0ll+s2[getid(x)]-sum2[y]-s1[getid(x)]+sum1[y]+(MOD<<1))%MOD;
for(int p=y+1;p<=prcnt&&1ll*pr[p]*pr[p]<=x;p++)
for(ll c=pr[p],e=1;c<=x;c*=pr[p],e++){
sum=(sum+1ll*(c%MOD)*((c-1)%MOD)%MOD*(calc(x/c,p)+(e>1)))%MOD;
} return sum;
}
int main(){
scanf("%lld",&n);lim=(int)sqrt(n);sieve(lim);init();
printf("%d
",(calc(n,0)+1)%MOD);
return 0;
}
总之,对于 OI 中大部分数论题,了解这几种筛法就够了,重要性排名大概是线筛复杂度 ok 就线筛,如果数据范围出到 (10^{10}) 那我们就先尝试杜教筛,如果杜教筛不可行就 Min_25
硬刚,其他的筛子譬如洲阁筛之类的等碰到毒瘤题再学吧……
7. 一些例题
9. P1587 [NOI2016] 循环之美
带家好,这是一个看到互质就直接莽两个反演然后愉快地死掉了的菜鸡
首先,根据小学奥数,(dfrac{x}{y}) 在 (k) 进制下是纯循环小数当且仅当 (yperp k),又因为我们不希望出现重复,所以我们枚举时也应有 (xperp y),因此
然后就到了我降智的时候了,我一看到两个互质就把它们反演开来,反演完之后又出现了互质就又反演了一次,反演了好几层之后发现不对劲就 quit 了
事实上碰到这样的题我们不应两次反演莽上去,考虑先反演第二个互质,那么有:
又 ([xperp yz]=[xperp y][xperp z]),因此
考虑记 (f(n,m,k)=sumlimits_{x=1}^nsumlimits_{y=1}^m[xperp y][yperp k])
那么上式可以写作
递归求解即可。
注意特判 (k=1) 的情况,此时 (f(n,m,k)=sumlimits_{x=1}^nsumlimits_{y=1}^m[xperp y]),整除分块+杜教筛走起,由于每次杜教筛求前缀和的位置都是 (n,m) 的关键点,因此总复杂度是 (mathcal O(n^{2/3}+ ext{递归次数})) 的,感性理解一下可知该复杂度可以通过此题
10. CF1097F Alex and a TV Show
这个题还算有意思,虽然 CF 上评分不过 2500……
首先由于所有运算都要放在 (mod 2) 意义下进行,因此可以考虑 bitset
,我们还注意到此题涉及 (gcd),因此也可以考虑莫比乌斯反演。具体来说,记 (a_{x,y}) 表示 (x) 集合里 (y) 出现次数 (mod 2),(b_{x,y}) 表示 (x) 集合里 (y) 的倍数出现次数 (mod 2),那么
套用一下倍数式的莫比乌斯反演:
因此考虑设 (w_{y,z}=mu(dfrac{z}{y})),如果 (y mid z) 那么 (w_{y,z}=0),那么:
因此我们只需要 bitset
维护 (b_x) 即可。
接下来考虑本题的几个操作:
- (1) 操作:令 (d_{x,y}=[xmid y]),那么直接令 (b_x=d_v)
- (2) 操作:相当于将两个集合 (b) 数组对应位上的数相加,取
xor
即可 - (3) 操作:注意到对于两个集合 (x,y),它们的积 (z) 的 (b_{z,v}=b_{x,v}b_{y,v}),而模 (2) 意义下的乘法等价于按位与,因此将两个集合取
and
即可。
11. LOJ #6053 简单的函数
Min_25 筛板子题。注意到对于奇质数 (x) 而言,都有 (f(x)=x-1) 为多项式,因此 Min_25 筛直接上,对于 (x=2) 的情况我们先假定当 (x=2) 时也有 (f(x)=x-1),最后答案加上 (2) 即可。
12. LOJ #6235 区间素数个数
Yet another 板子题……甚至第二步都不需要,就当练练熟练度罢……
13. LOJ #6625 时间复杂度
Yet another Min_25 筛板子……
注意到对于一个 (x) 而言,如果它是质数,那么在判断其是否是质数时会跑 (x-2) 次,否则会跑 (mnp_x-1) 次,也就是说答案即为 (sumlimits_{i=2}^nmnp_i-1-sumlimits_{i=2}^n[i ext{ is a prime}]) 两部分都可以 Min_25 筛求出,复杂度 (dfrac{n^{3/4}}{log n})
稍微贴个代码吧:
const int SQRT=333333;
const int MOD=20190601;
const int INV2=MOD+1>>1;
ll n;int lim,pr[SQRT/7+5],prcnt,vis[SQRT+5],sum[SQRT/7+5];
void sieve(int n){
for(int i=2;i<=n;i++){
if(!vis[i]) pr[++prcnt]=i,vis[i]=1;
for(int j=1;j<=prcnt&&pr[j]*i<=n;j++){
vis[pr[j]*i]=1;if(i%pr[j]==0) break;
}
}
for(int i=1;i<=prcnt;i++) sum[i]=(sum[i-1]+pr[i])%MOD;
}
ll pos[SQRT*2+5];int pos_n=0,id[SQRT+5];
int s0[SQRT*2+5],s1[SQRT*2+5];
int getid(ll x){return (x<=lim)?x:id[n/x];}
int main(){
scanf("%lld",&n);lim=(int)sqrt(n);sieve(lim);
for(int i=1;i<=lim;i++) pos[++pos_n]=i;
for(int i=lim;i;i--) if(n/i>lim) pos[id[i]=++pos_n]=n/i;
for(int i=1;i<=pos_n;i++){
s0[i]=(pos[i]-1)%MOD;
s1[i]=(1ll*((pos[i]+1)%MOD)*(pos[i]%MOD)%MOD*INV2%MOD-1+MOD)%MOD;
} int res=0;
for(int i=1;i<=prcnt;i++){
for(int j=pos_n;j&&pos[j]>=1ll*pr[i]*pr[i];j--){
s0[j]=(s0[j]-(s0[getid(pos[j]/pr[i])]-(i-1)+MOD)%MOD+MOD)%MOD;
s1[j]=(s1[j]-1ll*pr[i]*(s1[getid(pos[j]/pr[i])]-sum[i-1]+MOD)%MOD+MOD)%MOD;
}
for(ll c=pr[i],e=1;c<=n;c*=pr[i],e++){
res=(res+1ll*(((n/c<=pr[i])?0:(s0[getid(n/c)]-i))+(!!(e^1))+MOD)*pr[i])%MOD;
}
}
res=(0ll+res+s1[pos_n]-s0[pos_n]+MOD)%MOD;
printf("%d
",(res-(n%MOD)+1+MOD)%MOD);
return 0;
}
14. UOJ #188 【UR #13】Sanrd
一道不算太板的 Min_25 筛板子题(大雾
首先题目等价于要我们求 ([1,n]) 中所有数严格次大质因子之和,第一部分就照样算就好了,第二部分中既然质数的贡献不算在内,咱就不用考虑质数的贡献,只用考虑合数的贡献了。注意到我们枚举 ([1,x]) 中最小质因子 (>pr_y) 的数 (v) 的最小质因子的过程中,假设最小质因子为 (p),其次数为 (e),那么必然有若 (dfrac{v}{p^e}) 为质数,那么 (v) 严格次大质因子就是 (p),否则 (v) 严格次大质因子同 (dfrac{v}{p^e}) 严格次大质因子,后半部分就递归处理 (dfrac{x}{p^e}) 即可,前半部分就调用之前处理的值算出 ((p,dfrac{x}{p^e}]) 中质数个数然后乘上 (p) 即可算出它们的贡献,注意若 (e>1) 还要额外加上 (p^e),也就是 (dfrac{v}{p_e}=1) 的贡献。
15. HDU 6987 Cycle Binary
杜教筛好题,题解
16. P5518 [MtOI2019]幽灵乐团 / 莫比乌斯反演基础练习题
恶心题,题解
17. P3700 [CQOI2017]小Q的表格
hot tea,题解
18. P6271 [湖北省队互测2014]一个人的数论
首先推一波式子:
注意到后面那东西是关于 (n/d) 的 (k+1) 次多项式 (F(n/d)),因此我们考虑设 (F(x)=sumlimits_{i=0}^{k+1}f_ix^i),那么
显然 (g(d)=mu(d)d^{k-i}) 是个积性函数,于是
插出 (f_i) 之后用脚趾头计算即可。
至于怎么插 (f_i),可以拉格朗日,不过由于此题 (k) 数据范围小得惊人,因此龟速插值(高斯消元)(k^3) 也能过。。。。。。
参考资料:
- 数论基础 学习笔记,mol 数论大师 ycx
- 莫比乌斯反演与数论函数
- 杜教筛
- 关于 min_25 筛的入门以及复杂度证明