min-25 筛学习笔记
“ min-25 筛学习笔记 参考 好像有些地方把 , 搞混了,应该能看懂吧( ”
筛是一种求积性函数前缀和的筛法。即 ,要求 可以被快速算出。时间复杂度 ,空间复杂度 。
min-25 筛
预处理
计算 。定义 , 表示 的最小素约数。这里 和 不一样, 的每一个数都和 为素数时的计算方法一样,因为好算 。形象理解 表示埃氏筛第 次后还没筛出的数 之和。因为每个 都一定有素约数 。最后 即为所求。
所以 时,,因为 并没有多筛出的数。 时,考虑 的贡献。多出来的部分无非就是 的数,这些数显然不大于 ,且 。
因为 ,所以 。因为枚举了 ,这些数乘上 后 ,所以应当把多算的部分减去。
计算
我们现在知道 素数部分的贡献。设一个二元组 。
可以发现 。考虑计算 ,运用之前预处理的答案,素数部分的贡献是 。合数部分我们枚举最小的质因子 和幂次 :
边界是 ,。时间复杂度 。
例题
区间素数个数
因为 当 不是素数,所以这道题只需要算 ,且 。
#include <bits/stdc++.h>
typedef long long ll;
const int N = 4e6 + 50;
ll n, id1[N], id2[N], g[N], p[N], w[N], sqr;
int cnt, m;
bool vis[N];
void init(int m) {
for (int i = 2; i <= m; i++) {
if (!vis[i]) p[++cnt] = i;
for (int j = 1; j <= cnt && i * p[j] <= m; j++) {
vis[i * p[j]] = 1;
if (i % p[j] == 0) break;
}
}
}
int main() {
scanf("%lld", &n);
sqr = sqrt(n);
init(sqr);
for (ll l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
w[++m] = n / l;
g[m] = w[m] - 1;
if (w[m] <= sqr) id1[w[m]] = m;
else id2[r] = m;
}
for (int j = 1; j <= cnt; j++)
for (int i = 1; i <= m && p[j] * p[j] <= w[i]; i++) {
int k = (w[i] / p[j] <= sqr) ? id1[w[i] / p[j]] : id2[n / (w[i] / p[j])];
g[i] = g[i] - (g[k] - j + 1);
}
printf("%lld\n", g[1]);
return 0;
}
简单函数
因为除了 的所有约数都是奇数。可以令所有 ,特别的 加上 即可。直接套板子就可以了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5, MOD = 1e9 + 7, inv2 = (MOD + 1) / 2;
ll id1[N], id2[N], n, m, g[N], h[N], P[N], sum[N], w[N];
int cnt, tot;
bool vis[N];
void init() {
for (ll i = 2; i <= m; i++) {
if (!vis[i]) P[++cnt] = i, sum[cnt] = (sum[cnt - 1] + i) % MOD;
for (int j = 1; j <= cnt && P[j] * i <= m; j++) {
vis[P[j] * i] = 1;
if (i % P[j] == 0) break;
}
}
}
ll fpow(ll a, ll b) {
ll res = 1;
for (; b; b >>= 1, a = a * a % MOD) if (b & 1) res = res * a % MOD;
return res;
}
ll S(ll x, int y) {
if (x <= 1 || P[y] > x) return 0;
int k = (x <= m) ? id1[x] : id2[n / x];
ll res = (g[k] - sum[y - 1] - h[k] + y - 1 + MOD) % MOD;
if (y == 1) res += 2;
for (int i = y; i <= cnt && P[i] * P[i] <= x; i++) {
ll pow1 = P[i], pow2 = P[i] * P[i];
for (int e = 1; pow2 <= x; e++, pow1 = pow2, pow2 *= P[i]) {
ll tmp = (S(x / pow1, i + 1) * (P[i] ^ e) % MOD + (P[i] ^ (e + 1))) % MOD;
res = (res + tmp) % MOD;
}
}
return res;
}
int main() {
scanf("%lld", &n);
m = sqrt(n + 0.5);
init();
for (ll l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
w[++tot] = n / l;
h[tot] = w[tot] % MOD - 1;
g[tot] = w[tot] % MOD * (w[tot] + 1) % MOD * inv2 % MOD;
g[tot] = (g[tot] - 1 + MOD) % MOD;
if (w[tot] > m) id2[r] = tot;
else id1[w[tot]] = tot;
}
for (int j = 1; j <= cnt; j++)
for (int i = 1; i <= tot && P[j] * P[j] <= w[i]; i++) {
int k = (w[i] / P[j] <= m) ? id1[w[i] / P[j]] : id2[n / (w[i] / P[j])];
g[i] = (g[i] - (P[j] * ((g[k] - sum[j - 1] + MOD) % MOD)) % MOD + MOD) % MOD;
h[i] = (h[i] - h[k] + j - 1 + MOD) % MOD;
}
ll ans = (S(n, 1) + 1) % MOD;
printf("%lld\n", (ans + MOD) % MOD);
return 0;
}
Min-25 筛
。因为要求我们的 函数完全积性,所以令 ,。定义二元组 ,。
则 。
#include <cstdio>
#include <cmath>
typedef long long ll;
const int N = 4e6 + 5, MOD = 1e9 + 7;
const ll i6 = 166666668, i2 = 500000004;
ll n, id1[N], id2[N], su1[N], su2[N], p[N], sqr, w[N], g[N], h[N];
int cnt, m;
bool vis[N];
ll add(ll a, ll b) {a %= MOD, b %= MOD; return (a + b >= MOD) ? a + b - MOD : a + b;}
ll mul(ll a, ll b) {a %= MOD, b %= MOD; return a * b % MOD;}
ll dec(ll a, ll b) {a %= MOD, b %= MOD; return ((a - b) % MOD + MOD) % MOD;}
void init(int m) {
for (ll i = 2; i <= m; i++) {
if (!vis[i]) p[++cnt] = i, su1[cnt] = add(su1[cnt - 1], i), su2[cnt] = add(su2[cnt - 1], mul(i, i));
for (int j = 1; j <= cnt && i * p[j] <= m; j++) {
vis[p[j] * i] = 1;
if (i % p[j] == 0) break;
}
}
}
ll S(ll x, int y) {
if (p[y] > x || x <= 1) return 0;
int k = (x <= sqr) ? id1[x] : id2[n / x];
ll res = dec(dec(g[k], h[k]), dec(su2[y - 1], su1[y - 1]));
for (int i = y; i <= cnt && p[i] * p[i] <= x; i++) {
ll pow1 = p[i], pow2 = p[i] * p[i];
for (int e = 1; pow2 <= x; pow1 = pow2, pow2 *= p[i], e++) {
ll tmp = mul(mul(pow1, dec(pow1, 1)), S(x / pow1, i + 1));
tmp = add(tmp, mul(pow2, dec(pow2, 1)));
res = add(res, tmp);
}
}
return res;
}
int main() {
scanf("%lld", &n);
sqr = sqrt(n + 0.5) + 1;
init(sqr);
for (ll l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
w[++m] = n / l;
g[m] = mul(w[m] % MOD, (w[m] + 1) % MOD);
g[m] = mul(g[m], (2 * w[m] + 1) % MOD);
g[m] = mul(g[m], i6);
g[m] = dec(g[m], 1);
h[m] = mul(w[m] % MOD, (w[m] + 1) % MOD);;
h[m] = mul(h[m], i2);
h[m] = dec(h[m], 1);
(w[m] <= sqr) ? id1[w[m]] = m : id2[r] = m;
}
for (int j = 1; j <= cnt; j++)
for (int i = 1; i <= m && p[j] * p[j] <= w[i]; i++) {
int k = (w[i] / p[j] <= sqr) ? id1[w[i] / p[j]] : id2[n / (w[i] / p[j])];
g[i] = dec(g[i], mul(mul(p[j], p[j]), dec(g[k], su2[j - 1])));
h[i] = dec(h[i], mul(p[j], dec(h[k], su1[j - 1])));
}
//printf("%lld\n", g[1] - h[1]);
printf("%lld\n", add(S(n, 1), 1));
return 0;
}