【题目链接】
https://loj.ac/problem/6053
【题解】
min_25筛的模板题。
min_25筛可以解决以下一类积性函数的求和问题。
1.
f
(
x
)
(
x
∈
P
)
f(x)(x \in P)
f(x)(x∈P)可以通过多项式表达出来。
2.
f
(
x
k
)
(
x
∈
P
)
f(x^k)(x \in P)
f(xk)(x∈P)可以快速算出。
这道题中,
f
(
x
)
=
x
f(x) = x
f(x)=x^
1
=
x
−
1
(
x
∈
P
且
x
≠
2
)
1 = x - 1(x \in P 且x \neq 2)
1=x−1(x∈P且x̸=2)所以条件1满足,条件2显然也是满足的。
part1
我们先来考虑一个问题,如何求
F
(
T
)
=
∑
i
=
1
T
f
(
i
)
(
i
∈
P
,
T
∈
⌊
N
/
i
⌋
)
F(T)=\sum_{i=1}^{T}f(i)(i\in P,T\in \left\lfloor N/i\right\rfloor )
F(T)=∑i=1Tf(i)(i∈P,T∈⌊N/i⌋),T是N通过除法能得到的数,大约有
2
N
2 \sqrt{N}
2N种取值。
不难发现,
∑
i
=
1
N
f
(
i
)
(
i
∈
P
)
=
∑
i
=
1
N
i
−
1
(
i
∈
P
)
+
2
\sum_{i=1}^{N}f(i)(i\in P) = \sum_{i=1}^{N}i-1(i\in P) + 2
∑i=1Nf(i)(i∈P)=∑i=1Ni−1(i∈P)+2 因为只有2异或了后会加1。
那么我们要求的就是质数的和以及质数的个数。
首先筛出
1..
N
1..\sqrt{N}
1..N中的所有质数,记作
p
[
1
]
.
.
p
[
M
]
p[1]..p[M]
p[1]..p[M]。
记函数
G
k
(
n
,
m
)
G_{k}(n, m)
Gk(n,m)表示
∑
i
=
1
n
i
k
(
i
∈
P
或
i
的
最
小
质
因
子
>
p
[
m
]
)
\sum_{i = 1}^{n}i^k(i \in P或i的最小质因子>p[m])
∑i=1nik(i∈P或i的最小质因子>p[m])。
那么相当于求出
G
0
(
T
,
M
)
,
G
1
(
T
,
M
)
G_{0}(T,M),G_{1}(T,M)
G0(T,M),G1(T,M)
考虑从
G
k
(
n
,
m
−
1
)
G_{k}(n,m-1)
Gk(n,m−1)推到
G
k
(
n
,
m
)
G_{k}(n,m)
Gk(n,m),就是从中去除最小质因子是
p
[
m
]
p[m]
p[m]的,于是可以推出:
G
k
(
n
,
m
)
=
G
k
(
n
,
m
−
1
)
−
p
[
m
]
k
∗
(
G
k
(
⌊
n
/
p
[
m
]
⌋
,
m
−
1
)
−
G
k
(
p
[
m
]
−
1
,
m
−
1
)
)
G_{k}(n,m)=G_{k}(n,m-1)-p[m]^k*(G_{k}(\left\lfloor n/p[m]\right\rfloor,m-1)-G_{k}(p[m]-1,m-1))
Gk(n,m)=Gk(n,m−1)−p[m]k∗(Gk(⌊n/p[m]⌋,m−1)−Gk(p[m]−1,m−1))
相当于强制选出一个
p
[
m
]
p[m]
p[m],剩下的随便选,但要重新加上有比
p
[
m
]
p[m]
p[m]更小的质因子的方案。
边界条件是
G
k
(
n
,
0
)
=
∑
i
=
1
n
i
k
G_{k}(n,0)=\sum_{i=1}^{n}i^k
Gk(n,0)=∑i=1nik,可以
O
(
1
)
O(1)
O(1)计算。
由于我们只要求
2
N
2\sqrt{N}
2N个取值,可以将它们一起考虑。
具体来说,我们从前往后枚举每一个质数
p
[
1
]
.
.
p
[
m
]
p[1]..p[m]
p[1]..p[m]由于大的的取值不会影响小的,所以从后往前更新。
现在的复杂度是
N
∗
(
s
q
r
t
N
/
l
n
N
)
=
O
(
N
/
l
n
N
)
\sqrt{N}*(sqrt{N}/ln{\sqrt{N}})=O(N/ln{\sqrt{N}})
N∗(sqrtN/lnN)=O(N/lnN)
考虑优化,当
p
[
m
]
2
>
n
p[m]^2>n
p[m]2>n时,
G
k
(
n
,
m
)
=
G
k
(
n
,
m
−
1
)
G_{k}(n,m)=G_{k}(n,m-1)
Gk(n,m)=Gk(n,m−1),因为不可能存在一个有
p
[
m
]
p[m]
p[m]的因子但不是质数的数,所以每次有一段前缀不用算。
现在的复杂度是
∑
T
∑
i
=
1
m
[
p
[
i
]
2
≤
T
]
≈
O
(
N
3
/
4
/
l
n
N
)
\sum_{T}\sum_{i=1}^{m}[p[i]^2 \leq T]\approx O(N^{3/4}/ln{N})
∑T∑i=1m[p[i]2≤T]≈O(N3/4/lnN)?,我不会证,但肯定比
O
(
N
3
/
4
)
O(N^{3/4})
O(N3/4)小。
Part2
处理出了
F
F
F接下来就简单了。
记
S
(
n
,
m
)
S(n,m)
S(n,m)为
∑
i
=
1
n
f
(
i
)
[
i
的
最
小
质
因
子
≥
p
[
m
]
]
\sum_{i=1}^{n}f(i)[i的最小质因子\geq p[m]]
∑i=1nf(i)[i的最小质因子≥p[m]],我们要求的是
S
(
N
,
1
)
+
f
(
1
)
S(N,1)+f(1)
S(N,1)+f(1)。
由于我们已经知道了
F
F
F(质数的答案和)。现在要加入非质数的答案。还是考虑枚举质数:
S
(
n
,
m
)
=
F
(
n
)
−
F
(
p
[
m
]
−
1
)
+
∑
i
=
m
M
∑
j
=
1
p
[
i
]
j
+
1
≤
N
(
f
(
p
[
i
]
j
)
∗
S
(
n
/
p
[
i
]
j
,
i
+
1
)
+
f
(
p
[
i
]
j
+
1
)
)
S(n,m)=F(n)-F(p[m]-1)+\sum_{i=m}^{M}\sum_{j=1}^{p[i]^{j+1}\leq N}(f(p[i]^j)*S(n/p[i]^j,i+1)+f(p[i]^{j+1}))
S(n,m)=F(n)−F(p[m]−1)+∑i=mM∑j=1p[i]j+1≤N(f(p[i]j)∗S(n/p[i]j,i+1)+f(p[i]j+1))
由于
p
[
M
]
≤
N
p[M]\leq \sqrt{N}
p[M]≤N所以它的
F
F
F值也是已知的。
边界条件是
n
<
p
[
m
]
n<p[m]
n<p[m]时,值为0。
这个相当于是把上一部分的递归实现,所以复杂度与Part1相同。
总时间复杂度:
O
(
N
3
/
4
/
l
n
N
)
O(N^{3/4}/ln{N})
O(N3/4/lnN)一般能做到
1
e
10
1e10
1e10
【代码】(为了便于调试很多地方写的比较烦)
/* - - - - - - - - - - - - - - -
User : VanishD
problem : loj-6053
Points : Min_25
- - - - - - - - - - - - - - - */
# include <bits/stdc++.h>
# define ll long long
# define inf 0x3f3f3f3f
# define P 1000000007
# define N 1001000
using namespace std;
ll use[N], p[N], pre[N], bak[N], pre0[N], pre1[N], bak0[N], bak1[N];
ll sum, nt, cnt;
int pnum;
const ll inv2 = 5e8 + 4;
void get_p(int n){
use[1] = true;
for (int i = 2; i <= n; i++){
if (!use[i]) p[++pnum] = i;
for (ll j = 1; j <= pnum && 1ll * i * p[j] <= n; j++){
use[i * p[j]] = true;
if (i % p[j] == 0) break;
}
}
}
ll F(ll p, int k){
if (k == 0) return 1;
return p ^ k;
}
void get_Pow0(){
for (int i = 1; i <= nt; i++)
pre0[i] = i, bak0[i] = sum / i;
for (int i = 1, prep = 1, bakp = nt; i <= pnum; i++){
while (prep <= nt && p[i] * p[i] > prep) prep++;
while (bakp >= 1 && p[i] * p[i] > sum / bakp) bakp--;
for (int j = 1; j <= bakp; j++){
ll x = sum / j;
ll tmp1 = (x <= nt) ? pre0[x] : bak0[sum / x],
tmp2 = (x / p[i] <= nt) ? pre0[x / p[i]] : bak0[sum / (x / p[i])],
tmp3 = pre0[p[i] - 1];
bak0[j] = (tmp1 - 1 * (tmp2 - tmp3) + P) % P;
}
for (int j = nt; j >= prep; j--){
ll x = j;
ll tmp1 = (x <= nt) ? pre0[x] : bak0[sum / x],
tmp2 = (x / p[i] <= nt) ? pre0[x / p[i]] : bak0[sum / (x / p[i])],
tmp3 = pre0[p[i] - 1];
pre0[j] = (tmp1 - 1 * (tmp2 - tmp3) + P) % P;
}
}
for (int i = 1; i <= nt; i++) pre0[i] -= 1, bak0[i] -= 1;
}
void get_Pow1(){
for (int i = 1; i <= nt; i++){
pre1[i] = 1ll * i * (i + 1) % P * inv2 % P;
ll tmp = (sum / i) % P;
bak1[i] = tmp * (tmp + 1) % P * inv2 % P;
}
for (int i = 1, prep = 1, bakp = nt; i <= pnum; i++){
while (prep <= nt && p[i] * p[i] > prep) prep++;
while (bakp >= 1 && p[i] * p[i] > sum / bakp) bakp--;
for (int j = 1; j <= bakp; j++){
ll x = sum / j;
ll tmp1 = (x <= nt) ? pre1[x] : bak1[sum / x],
tmp2 = (x / p[i] <= nt) ? pre1[x / p[i]] : bak1[sum / (x / p[i])],
tmp3 = pre1[p[i] - 1];
bak1[j] = (tmp1 - p[i] * (tmp2 - tmp3) % P + P) % P;
}
for (int j = nt; j >= prep; j--){
ll x = j;
ll tmp1 = (x <= nt) ? pre1[x] : bak1[sum / x],
tmp2 = (x / p[i] <= nt) ? pre1[x / p[i]] : bak1[sum / (x / p[i])],
tmp3 = pre1[p[i] - 1];
pre1[j] = (tmp1 - p[i] * (tmp2 - tmp3) % P + P) % P;
}
}
for (int i = 1; i <= nt; i++) pre1[i] -= 1, bak1[i] -= 1;
}
ll get_s(ll n, int m){
if (n <= 1 || n < p[m]) return 0;
ll ans = ((n > nt) ? bak[sum / n] : pre[n]) - pre[p[m] - 1];
for (int i = m; i <= pnum && n >= 1ll * p[i] * p[i]; i++)
for (ll j = 1, k = p[i]; k * p[i] <= n; j++, k *= p[i])
ans = (ans + F(p[i], j) * get_s(n / k, i + 1) + F(p[i], j + 1)) % P;
return ans;
}
int main(){
ll n;
scanf("%lld", &n);
sum = n; nt = sqrt(n);
get_p(nt);
get_Pow0(); get_Pow1();
for (int i = 1; i <= nt; i++){
if (i == 1) pre[i] = 0;
else pre[i] = (pre1[i] - pre0[i] + 2 + P) % P;
bak[i] = (bak1[i] - bak0[i] + 2 + P) % P;
}
p[pnum + 1] = nt + 1;
printf("%lld\n", (get_s(n, 1) + 1) % P);
return 0;
}