25筛模板题
使用min_25的三个条件:
1.
f
(
x
)
f(x)
f(x) 是积性函数
2.
f
(
p
)
f(p)
f(p) 是一个简单多项式,p是素数
3.
f
(
p
x
)
f(p^x)
f(px) 容易求得(指
O
(
1
)
O(1)
O(1)时间内求得),p是素数
min_25筛用来求一些积性函数的前缀和
∑
i
=
1
n
f
(
i
)
\sum_{i = 1}^nf(i)
∑i=1nf(i)
其大致思想是:将答案拆成质数和合数部分:
∑
i
=
1
∣
P
∣
f
(
p
i
)
+
∑
i
=
1
n
f
(
i
)
[
i
!
=
1
,
i
∉
P
]
\sum_{i = 1}^{|P|}f(p_i) + \sum_{i = 1} ^ nf(i)[i != 1 ,i \notin P]
∑i=1∣P∣f(pi)+∑i=1nf(i)[i!=1,i∈/P]
如何求质数部分的答案:维护
g
(
n
,
j
)
g(n,j)
g(n,j)表示
∑
i
=
1
n
f
(
i
)
[
i
是
素
数
或
i
的
最
小
素
因
子
大
于
p
j
]
\sum_{i = 1} ^ nf(i)[i 是素数 或i的最小素因子大于 p_j]
∑i=1nf(i)[i是素数或i的最小素因子大于pj],转移:
g
(
n
,
j
)
=
g
(
n
,
j
−
1
)
−
f
(
p
j
)
∗
(
g
(
⌊
n
p
j
⌋
,
j
−
1
)
−
∑
i
=
1
j
−
1
f
(
p
i
)
)
g(n,j) = g(n,j - 1) - f(p_j)*(g(\lfloor\frac{n}{p_j}\rfloor,j - 1) - \sum_{i = 1}^{j - 1} f(p_i))
g(n,j)=g(n,j−1)−f(pj)∗(g(⌊pjn⌋,j−1)−i=1∑j−1f(pi))
即
g
(
n
,
j
−
1
)
g(n,j - 1)
g(n,j−1) 扣掉以
p
j
p_j
pj作为最小素因子的贡献,那么显然
p
j
≤
n
p_j \leq \sqrt n
pj≤n,因此只需要筛出
n
\sqrt n
n范围内的素数用来转移。
(基于容斥的转移,感性理解一下)
显然可以DP,第二维可以滚动节约空间,由于转移要用到
⌊
n
p
j
⌋
\lfloor\frac{n}{p_j}\rfloor
⌊pjn⌋,而
⌊
n
p
j
⌋
\lfloor\frac{n}{p_j}\rfloor
⌊pjn⌋最多只有
2
n
2\sqrt n
2n 个值,可以预处理将这些值取出来离散化,然后递推DP转移即可筛出
g
g
g
考虑 g ( n , 0 ) g(n,0) g(n,0)的取值,观察一下会发现 g ( n , j ) g(n,j) g(n,j)是由 g ( n , j − 1 ) g(n,j - 1) g(n,j−1)去掉最小素因子为 p j p_j pj的合数的贡献,这实际上是借用了埃筛的思想,最后 g ( n , ∣ P ∣ ) g(n,|P|) g(n,∣P∣)就是[1,n] 所有参数为素数的 f f f函数值的和, g ( n , 0 ) g(n,0) g(n,0)应取所有数贡献(即将所有数都当成素数求 ∑ i = 1 n f ( i ) \sum_{i = 1}^nf(i) ∑i=1nf(i))
sqr = sqrt(n);
for(ll i = 1,j; i <= n; i = j + 1) {
j = n / (n / i);
w[++tot] = n / i;
ll p = n / i % mod;
g1[tot] = (p * (p + 1) / 2 % mod - 1 + mod) % mod;
g2[tot] = (p - 1) % mod;
if(n / i <= sqr) id1[n / i] = tot;
else id2[n / (n / i)] = tot;
}
for(int i = 1; i <= num; i++) {
for(int j = 1; j <= tot && 1ll * pri[i] * pri[i] <= w[j]; j++) {
ll t = w[j] / pri[i];
ll k = t <= sqr ? id1[t] : id2[n / t];
g1[j] -= (g1[k] - sum1[i - 1] + mod) % mod * pri[i] % mod;
g2[j] -= (g2[k] - sum2[i - 1] + mod) % mod;
if(g1[j] < 0) g1[j] += mod;
if(g2[j] < 0) g2[j] += mod;
}
}
现在考虑来筛
∑
i
=
1
n
f
(
i
)
\sum_{i = 1}^n f(i)
∑i=1nf(i),定义
S
(
n
,
j
)
=
∑
i
=
1
n
f
(
i
)
[
i
的
最
小
素
因
子
>
p
j
]
S(n,j) = \sum_{i = 1}^nf(i)[i的最小素因子 > p_j]
S(n,j)=∑i=1nf(i)[i的最小素因子>pj]
将式子分为质数和合数部分:
S
(
n
,
j
)
=
g
(
n
,
∣
P
∣
)
−
∑
i
=
1
j
f
(
p
i
)
+
∑
k
=
j
+
1
,
p
k
≤
n
∑
e
=
1
,
p
k
e
≤
n
f
(
p
k
e
)
∗
(
S
(
⌊
n
p
k
e
⌋
,
k
)
+
[
e
!
=
1
]
)
S(n,j) = g(n,|P|) - \sum_{i = 1}^jf(p_i) + \sum_{k = j + 1,p_k \leq \sqrt n}\sum_{e = 1,p_k^e \leq n}f(p_k^e)*(S(\lfloor\frac{n}{p_k^e}\rfloor,k) + [e != 1])
S(n,j)=g(n,∣P∣)−i=1∑jf(pi)+k=j+1,pk≤n∑e=1,pke≤n∑f(pke)∗(S(⌊pken⌋,k)+[e!=1])
左边部分即质数部分的和,因为要求最小素因子 >
p
j
p_j
pj,扣掉
≤
p
j
\leq p_j
≤pj的质数的贡献。
右边部分即合数部分的和,枚举最小素因子
p
k
p_k
pk,即最小素因子的幂次
e
e
e,将
f
(
p
k
e
)
f(p_k^e)
f(pke)提出来进行转移,因为
f
f
f函数是一个积性函数,提出来计算不会影响答案,因为
f
(
p
k
e
)
f(p_k^e)
f(pke)也被筛掉了,后面再加上,当
e
=
1
e = 1
e=1时,加上的是一个质数的贡献,这个贡献在前面已经加上,这里特判 e != 1
最后答案就是
S
(
n
,
0
)
+
f
(
1
)
S(n,0) + f(1)
S(n,0)+f(1),由于 1 既不是质数也不是合数,因此最后要加上 1 的贡献。
S
(
n
,
j
)
S(n,j)
S(n,j) 可以递归求,出于玄学,重复计算的项并不多(或没有重复计算),不用记忆化。
那么这题的题解:
当 p 为质数且 p != 2,
f
(
p
)
=
p
−
1
f(p) = p - 1
f(p)=p−1
当 p = 2,
f
(
p
)
=
p
+
1
f(p) = p + 1
f(p)=p+1
将素数项都考虑成 p - 1,最后特判 2,把答案加回来。
按套路将答案贡献拆成素数部分和合数部分。
将素数部分的贡献拆成两个部分:
∑
i
=
1
∣
P
∣
f
(
p
i
)
=
∑
i
=
1
∣
P
∣
p
i
−
∑
i
=
1
∣
P
∣
1
\sum_{i = 1}^{|P|}f(p_i) = \sum_{i = 1}^{|P|}p_i - \sum_{i = 1}^{|P|}1
∑i=1∣P∣f(pi)=∑i=1∣P∣pi−∑i=1∣P∣1
这两个部分都是积性函数,可以分别递推求两个部分的
g
(
n
,
∣
P
∣
)
g(n,|P|)
g(n,∣P∣)
然后按照套路递归求
S
(
n
,
j
)
S(n,j)
S(n,j) 即可
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
const int mod = 1e9 + 7;
int num,tot;
bool ispri[maxn];
ll pri[maxn],sum1[maxn],sum2[maxn];
ll w[maxn],g1[maxn],g2[maxn],id1[maxn],id2[maxn];
ll n,sqr;
void sieve(int n) {
ispri[0] = ispri[1] = true;
num = 0;
for(int i = 2; i <= n; i++) {
if(!ispri[i]) {
pri[++num] = i;
sum1[num] = (sum1[num - 1] + i) % mod;
sum2[num] = sum2[num - 1] + 1;
}
for(int j = 1; j <= num && i * pri[j] <= n; j++) {
ispri[i * pri[j]] = true;
if(i % pri[j] == 0) break;
}
}
}
ll S(ll x,int y) {
if(pri[y] >= x) return 0;
ll k = x <= sqr ? id1[x] : id2[n / x];
ll ans = ((g1[k] - sum1[y] - g2[k] + sum2[y]) % mod + mod) % mod;
if(x >= 2 && y == 0) ans += 2;
for(int i = y + 1; i <= num && 1ll * pri[i] * pri[i] <= x; i++) {
for(ll e = 1,pe = pri[i]; pe <= x; e++,pe = pe * pri[i]) {
ans += (pri[i] ^ e) % mod * (S(x / pe,i) + (e != 1)) % mod;
ans %= mod;
}
}
return ans;
}
int main() {
sieve(maxn - 10);
scanf("%lld",&n);
sqr = sqrt(n);
for(ll i = 1,j; i <= n; i = j + 1) {
j = n / (n / i);
w[++tot] = n / i;
ll p = n / i % mod;
g1[tot] = (p * (p + 1) / 2 % mod - 1 + mod) % mod;
g2[tot] = (p - 1) % mod;
if(n / i <= sqr) id1[n / i] = tot;
else id2[n / (n / i)] = tot;
}
for(int i = 1; i <= num; i++) {
for(int j = 1; j <= tot && 1ll * pri[i] * pri[i] <= w[j]; j++) {
ll t = w[j] / pri[i];
ll k = t <= sqr ? id1[t] : id2[n / t];
g1[j] -= (g1[k] - sum1[i - 1] + mod) % mod * pri[i] % mod;
g2[j] -= (g2[k] - sum2[i - 1] + mod) % mod;
if(g1[j] < 0) g1[j] += mod;
if(g2[j] < 0) g2[j] += mod;
}
}
ll res = (S(n,0) + 1) % mod;
printf("%lld\n",res);
return 0;
}