Min_25

hjm0703 Lv2

引入

首先这个算法是为了解决求解积性函数求前缀和: 解决的。

其有两个前提:

  • 所有的 必须能否被表达为一个类似 。
  • 所有的 在知道 的情况下能够快速求出 。

其算法的大致思路就是分成 质数 和 合数 分别解决,如下:

( 表示 的最小质因子)

第一部分十分好理解;而第二部分意思就是 枚举其质因子,那么合数就可以表示为 ,当然由于 必须等于 ,同时满足不能算重,因此需要要求 中不能包含 ,相当于 。

因此我们分成两部分完成: 质数,合数。

质数部分

这里目前我们的目标是求出对于所有质数的前缀和,但是 ~ 的质数筛都筛不出来。

我们令 表示从 ~ 中所有 满足 “ 是质数 或 的最小质因子大于 ” 的 的和。

此时这个 就和质数位置的 中的某一项相同,并且 是一个完全积性函数。

那么我们尝试找出 和 的关系。相当与从 中取出那些最小质因子等于 的合数。

那么其实就是剔除了一个 之后质因子大于等于 的数。

(后面那个 是为了去除所有质数)

然后后面的那个 就相当于前 个质数的 次方和。后面命名为

但是这个 还是很大阿,首先 因为 (最小质因子性质),并且对于 一直都是除去 ,因此其真实只有 个取值,可以写一个离散化。这里真实跑下来是比较快的。

此时如果距离 最近的 是第 个质数,那么 ~ 中质数 次方前缀和就是 。

然后把每一个 对应的 加上系数就是真实的我们求解的前缀和,这里,命名为 $d(x) = ag_0(x) + bg_1(x) + c~g_2(x) \dots$。

合数部分

此时我们令 表示对于 ~ 中的数,其最小质因子大于 的函数和。

那么同样枚举最小质因子 ,就可以得到:

这里这个 和上面相同,因为 也是合数。

可以发现, 就是答案,递归求解即可。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <bits/stdc++.h>
using namespace std;

#define int long long
/*~~~~~~~~~~~~~~~~~~~~ Boundary Line ~~~~~~~~~~~~~~~~~~~~*/
const int inv2 = 500000004, inv3 = 333333336;
const int N = 2e5+5, mod = 1e9+7;

/*~~~~~~~~~~~~~~~~~~~~ Boundary Line ~~~~~~~~~~~~~~~~~~~~*/
inline int norm_mut(int& x) { return x = (x%mod+mod)%mod; }
inline int norm(int x) { return (x%mod+mod)%mod; }

namespace Min_25 {
int n, b;

bitset<N> vis;
int tot, pri[N];

int val[N];
int s1[N], s2[N]; // 质数前缀和
int g1[N], g2[N];

int cnt, val1[N], val2[N]; // 所有可能产生的数
int& id(int x) {
if(x < b) return val1[x];
else return val2[n/x];
}

void build(int m) {
n=m, b=sqrt(m);

for(int i=2; i<=b; i++) {
if(vis[i] == 0) {
pri[++tot] = i;
norm_mut( s1[tot] = s1[tot-1] + i );
norm_mut( s2[tot] = s2[tot-1] + i*i%mod );
}

for(int j=1; j<=tot; j++) {
if(i * pri[j] > b) break;
vis[i * pri[j]] = 1;
if(i % pri[j] == 0) break;
}
}

// Min_25 Main

for(int l=1, r; l<=m; l=r+1) {
r = min(m, (m / (m/l)));
int i = (id(m/l) = ++cnt), j = (m/l)%mod;
val[cnt] = m/l; // ! 注意以下这里不能 %mod

norm_mut( g1[i] = j * (j+1)%mod * inv2%mod - 1 );
norm_mut( g2[i] = j * (j+1)%mod * (2*j+1)%mod * inv3%mod * inv2 %mod - 1 );
}

for(int i=1; i<=tot; i++) {
int p = pri[i];
for(int j=1; j<=cnt && p*p <= val[j]; j++) {
int k = id(val[j] / p);
norm_mut( g1[j] -= p * (g1[k] - s1[i-1]) %mod );
norm_mut( g2[j] -= p * p %mod * (g2[k] - s2[i-1]) %mod );
}
}
}

int f(int x) { return norm(x*x%mod - x); }

int S(int n, int x) {
if(n <= pri[x]) return 0;
int k = id(n);
int ans = norm((g2[k]-g1[k]) - (s2[x]-s1[x]));
for(int i=x+1; i<=tot && pri[i]*pri[i] <= n; i++) {
for(int e=1, pe=pri[i]; pe<=n; e++, pe*=pri[i]) {
norm_mut( ans += f(pe%mod) * (S(n/pe, i) + (e != 1)) %mod );
}
}
return ans;
}
};
/*~~~~~~~~~~~~~~~~~~~~ Boundary Line ~~~~~~~~~~~~~~~~~~~~*/
signed main() {
int n; cin>>n;
Min_25::build(n);
cout<<Min_25::S(n, 0)+1<<'\n'; // 这里 +1 是因为前面没有算 f(1)
return 0;
}
  • 标题: Min_25
  • 作者: hjm0703
  • 创建于 : 2026-09-12 16:05:22
  • 更新于 : 2026-08-16 06:38:16
  • 链接: https://hjm-blog.de5.net/2026/09/12/min_25/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论