参考博客
前置
m i n 2 5 min_25 min25就是一种能在低于线性复杂度求积性函数 f ( x ) f(x) f(x)前缀和的筛法
复杂度大概为 O ( n 3 4 l o g n ) O(\frac{n^{\frac 3 4}}{log_n}) O(lognn43)(不会证,不过朱老大论文里有证明(看不懂))
要求 f ( x ) f(x) f(x)是一个积性函数且可以看做若干完全积性函数的组合(或者某些特殊的使得 f ( p k ) f(p^k) f(pk)可以处理的),而且对于素数 p p p, f ( p ) f(p) f(p)和 f ( p k ) f(p^k) f(pk)可以快速得到
g g g
设 P P P表示素数集合, M i n k Min_k Mink表示 k k k的最小质因子
p r i pr_i pri表示从小到大第 i i i个素数
首先考虑求出 ∑ i = 1 n [ i ∈ P ] f ( i ) \sum_{i=1}^{n}[i\in P] f(i) ∑i=1n[i∈P]f(i)
定义 g ( n , i ) = ∑ j = 1 n [ j ∈ P o r M i n j > p r i ] f ( j ) g(n,i)=\sum_{j=1}^{n}[j\in P{or}Min_j>pr_i] f(j) g(n,i)=∑j=1n[j∈PorMinj>pri]f(j)
仔细瞪一下这个函数
我们发现从 g ( n , i − 1 ) g(n,i-1) g(n,i−1)到 g ( n , i ) g(n,i) g(n,i)
我们就相当于做埃氏筛的过程,用 p r i pr_i pri把 [ 1 , n ] [1,n] [1,n]所有还没有被筛去的 p r i pr_i pri的倍数筛去
实际上 g ( n , i ) g(n,i) g(n,i)就是对 [ 1 , n ] [1,n] [1,n]用前 i i i个素数做一次埃氏筛后剩下的数 f f f之和
现在考虑怎么从 g ( n , i − 1 ) g(n,i-1) g(n,i−1)推到 g ( n , i ) g(n,i) g(n,i)
首先如果 p r i 2 > n pr_{i}^2> n pri2>n那显然不会有筛去任何数
否则考虑哪些数被删去了
一定是含质因子
p
i
p_i
pi,而且除去
p
i
p_i
pi后最小质因子一定大于
p
i
p_i
pi
也就是减去
f
(
p
i
)
×
g
(
n
p
i
,
i
−
1
)
f(p_i)\times g(\frac n {p_i},i-1)
f(pi)×g(pin,i−1)
又由于我们把 p r 1 pr_1 pr1~ p r i − 1 pr_{i-1} pri−1这些素数的 f f f值减去了,所以要再加一个 ∑ j = 1 i − 1 f ( p j ) \sum_{j=1}^{i-1}f(p_j) ∑j=1i−1f(pj)
所以
g
(
n
,
i
)
=
{
g
(
n
,
i
−
1
)
n
<
p
i
2
g
(
n
,
i
−
1
)
−
f
(
p
i
)
×
[
g
(
n
p
i
,
i
−
1
)
−
∑
j
=
1
i
−
1
f
(
p
j
)
]
p
i
2
≤
n
g
(
n
,
i
)
=
n
i
=
0
g(n,i)= \begin{cases} g(n,i-1) && n<p_i^2\\ g(n,i-1)-f(p_i)\times[g(\frac n{p_i},i-1)-\sum_{j=1}^{i-1}f(p_j)] && p_i^2\le n\\ g(n,i)=n && i=0 \end{cases}
g(n,i)=⎩⎪⎨⎪⎧g(n,i−1)g(n,i−1)−f(pi)×[g(pin,i−1)−∑j=1i−1f(pj)]g(n,i)=nn<pi2pi2≤ni=0
由于最后实际要求的是 g ( n , ∣ P ∣ ) g(n,|P|) g(n,∣P∣),我们只需要开一维就可以了,第二维可以类似滚动数组滚掉
又由于大于 n \sqrt n n的质因子只有一个
所以我们可以记 f 1 [ i ] f1[i] f1[i]表示 g ( i , j ) g(i,j) g(i,j), f 2 [ i ] f2[i] f2[i]表示 g ( n i , j ) g(\frac n i,j) g(in,j)
这样空间复杂度只有 O ( n ) O(\sqrt n) O(n)
代码:当
f
(
p
)
=
1
f(p)=1
f(p)=1即求素数个数时
代码实现比较精细
可以证明这样复杂度是
O
(
n
3
4
l
o
g
n
)
O(\frac{n^{\frac 3 4}}{log_n})
O(lognn43)
int lim=sqrt(n);
for(int i=1;i<=lim;i++)f1[i]=i-1,f2[i]=n/i-1;
for(int p=2;p<=lim;p++){
if(f1[p]==f1[p-1])continue;
for(int i=1;i<=lim/p;i++)f2[i]-=f2[i*p]-f1[p-1];
for(int i=lim/p+1;1ll*i*p*p<=n&&i<=lim;i++)f2[i]-=f1[n/i/p]-f1[p-1];
for(int i=lim;i>=1ll*p*p;i--)f1[i]-=f1[i/p]-f1[p-1];
}
return f2[1];
但是会跑的很慢
你找一照发现问题很简单
卡一下数组枚举和除法以及一些常用变量就会快
5
、
6
5、6
5、6倍
lim=sqrt(n);
for(i=1;i<=lim;++i)f1[i]=i-1,f2[i]=n/i-1,inv[i]=1.0/i;
for(p=2;p<=lim;++p){
if(f1[p]==f1[p-1])continue;
x0=f1[p-1],w1=lim/p,w2=min(lim,(n/p/p)),dd=n/p;
for(i=1;i<=w1;++i)f2[i]+=x0-f2[i*p];
for(i=lim/p+1;i<=w2;++i)f2[i]+=x0-f1[(int)(inv[i]*dd+1e-7)];
for(i=lim;i>=1ll*p*p;--i)f1[i]+=x0-f1[(int)(inv[p]*i+1e-7)];
}
return f2[1];
可以注意到由于是直接把 f ( p ) f(p) f(p)拆出去,所以必须要 f f f为完全积性函数或者是质因子个数这种特殊的函数,如果是积性且为若干完全积性函数线性组合可以分开维护最后组合起来
S S S
现在我们已经求出了 ∑ i = 1 n [ i ∈ P ] f ( i ) \sum_{i=1}^{n}[i\in P] f(i) ∑i=1n[i∈P]f(i)
考虑如何计算合数的答案
设 S ( n , i ) = ∑ j = 1 n [ M i n j ≥ p r i ] f ( j ) S(n,i)=\sum_{j=1}^n [Min_j\geq pr_i] f(j) S(n,i)=∑j=1n[Minj≥pri]f(j)
即满足所有质因子大于等于 p i p_i pi的数的 f f f之和
那么显然
∑
i
=
1
n
f
(
i
)
=
S
(
n
,
1
)
+
f
(
1
)
\sum_{i=1}^{n} f(i)=S(n,1)+f(1)
∑i=1nf(i)=S(n,1)+f(1)
考虑如何类似的递推
首先素数的答案已经算出来了
显然的是
g
(
n
,
∣
P
∣
)
−
∑
j
=
1
i
−
1
f
(
p
r
j
)
g(n,|P|)-\sum_{j=1}^{i-1}f(pr_j)
g(n,∣P∣)−∑j=1i−1f(prj)(因为
S
S
S要求
M
i
n
j
≥
p
r
i
Min_j\ge pr_i
Minj≥pri)
考虑枚举合数的最小质因子和次数,直接算就是了
S ( n , i ) = g ( n , ∣ P ∣ ) − ∑ j = 1 i − 1 f ( p r j ) + ∑ j = i p r j 2 ≤ n ∑ x s = 1 p r j x s + 1 ≤ n S ( n p r j x s , j + 1 ) × f ( p r j x s ) + f ( p r j x s + 1 ) S(n,i)=g(n,|P|)-\sum_{j=1}^{i-1}f(pr_j)+\sum_{j=i}^{pr_j^2\le n}\sum_{xs=1}^{pr_j^{xs+1}\le n}S(\frac{n}{pr_j^{xs}},j+1)\times f(pr_j^{xs})+f(pr_j^{xs+1}) S(n,i)=g(n,∣P∣)−j=1∑i−1f(prj)+j=i∑prj2≤nxs=1∑prjxs+1≤nS(prjxsn,j+1)×f(prjxs)+f(prjxs+1)
关于这部分的复杂度:
论文里说的是
n
3
4
l
o
g
\frac{n^{\frac 3 4}}{log}
logn43
好像基于的关于质数的分析在
1
0
12
10^{12}
1012内是满足的
更大范围内应该说是
n
1
−
ϵ
n^{1-\epsilon}
n1−ϵ
不过更大范围也跑不过去的
然后就没了
代码看例题里的就可以了
例题: