题目

对于100%的数据,n<=1e10
思路来源
https://www.cnblogs.com/cjyyb/p/9185110.html
题解
首先,考虑质数,即c=1的情形,f(2)=3,其余f(p)=p-1,
这里就先统一f(p)=p-1处理,然后对第一个质数特判加2
表示
到
中,满足
是质数,或最小素因子大于第
个质数的,
的和
由于f(p)=p-1,所以这里拆成两部分,
g[]用来算前面一项,质数p的前缀和,h[]用来算后面一项,质数的个数即1的前缀和
两个数组求法是一样的,然后第二维可以根据增序迭代滚动掉
那么,
即把所有数都先当质数来算,求得一个假的值,后续再不断的迭代,把那些合数的减掉
初始时,,
实际上,本题只用到第二行的式子,且根据g的定义,
里只有那些质数的前缀和,所以有
根据这个固定转移式,发现需要n/Pj的值,那么套一下数论分块,
先把第一维的值弄出来,个数约为个,
则用id1和id2记录一下这些下标,相当于存到两个大小为的桶里,来进行离散化
for (ll l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
w[++c] = n / l;
h[c] = (w[c] - 1) % mod;
g[c] = (w[c] % mod) * ((w[c] + 1) % mod) % mod * inv2 % mod - 1;
if (w[c] <= Sqr)
id1[w[c]] = c;
else
id2[r] = c;
}
然后,正序处理g,迭代|P|次,即tot次,
最后的g[c]为g(n,|P|),即为所求,
for (int j = 1; j <= tot; ++j) {
for (int i = 1; i <= c && pri[j] * pri[j] <= w[i]; ++i) {
int k = (w[i] / pri[j] <= Sqr) ? id1[w[i] / pri[j]] : id2[n / (w[i] / pri[j])];
g[i] =(g[i]- 1ll * pri[j] * (g[k] - sp[j - 1]) % mod) % mod;
h[i] =(h[i]-(h[k] - j + 1))% mod;
}
}
然后,考虑求S,这里采用递归,并且不需要记忆化,
先考虑一下S(n,j)的初值,也就是所有满足条件的质数的答案
前期做题还不熟练,把概念再粘过来
,
即满足最小质因数大于等于
的
之和
这个答案是
然后根据
需要枚举后面两层sum,即一层最小质因子Pk,另一层是其幂次e,递归求答案
然后j=1时,需要把质数为2的特殊考虑,把和式的值+2
代码
loj神奇的自动缩进
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10, mod = 1e9 + 7, inv2 = (mod + 1) / 2;
ll n, Sqr, w[N];
ll pri[N], id1[N], id2[N], h[N], g[N], c;
bool zs[N];
int tot, sp[N];
void pre(int n) {
zs[1] = true;
for (int i = 2; i <= n; ++i) {
if (!zs[i]) {
pri[++tot] = i;
sp[tot] = (sp[tot - 1] + i) % mod;
}
for (int j = 1; j <= tot && i * pri[j] <= n; ++j) {
zs[i * pri[j]] = true;
if (i % pri[j] == 0)
break;
}
}
}
int S(ll x, int y) {
if (x <= 1 || pri[y] > x)
return 0;
int k = (x <= Sqr) ? id1[x] : id2[n / x], ret = (g[k] - sp[y - 1] - h[k] + y - 1) % mod;
if (y == 1)
ret += 2;
for (int i = y; i <= tot && 1ll * pri[i] * pri[i] <= x; ++i) {
ll t1 = pri[i], t2 = 1ll * pri[i] * pri[i];
for (int e = 1; t2 <= x; ++e, t1 = t2, t2 *= pri[i]) {
ret = (ret + ((1ll * S(x / t1, i + 1) * (pri[i] ^ e) % mod + (pri[i] ^ (e + 1)) % mod))) % mod;
}
}
return ret;
}
int main() {
scanf("%lld", &n);
Sqr = sqrt(n);
pre(Sqr);
for (ll l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
w[++c] = n / l;
h[c] = (w[c] - 1) % mod;
g[c] = (w[c] % mod) * ((w[c] + 1) % mod) % mod * inv2 % mod - 1;
if (w[c] <= Sqr)
id1[w[c]] = c;
else
id2[r] = c;
}
for (int j = 1; j <= tot; ++j) {
for (int i = 1; i <= c && pri[j] * pri[j] <= w[i]; ++i) {
int k = (w[i] / pri[j] <= Sqr) ? id1[w[i] / pri[j]] : id2[n / (w[i] / pri[j])];
g[i] = (g[i] - 1ll * pri[j] * (g[k] - sp[j - 1]) % mod) % mod;
h[i] = (h[i] - (h[k] - j + 1)) % mod;
}
}
int ans = S(n, 1) + 1;
printf("%d\n", (ans + mod) % mod);
return 0;
}
本文详细解析了一种求解特定条件下质数和合数和的算法,通过预处理质数及其前缀和,结合数论分块与递归策略,实现了高效求解。适用于数学竞赛和算法挑战。
366

被折叠的 条评论
为什么被折叠?



