Min_25 筛
由于其由 Min_25 发明并最早开始使用,故称「Min_25 筛」。
从此种筛法的思想方法来说,其又被称为「Extended Eratosthenes Sieve」。
其可以在
要求:
记号¶
- 如无特别说明,本节中所有记为
p x / y := \left\lfloor\frac{x}{y}\right\rfloor \operatorname{isprime}(n) := [ |\{d : d \mid n\}| = 2 ] n 1 0 p_{k} k p_{1} = 2, p_{2} = 3 p_{0} = 1 \operatorname{lpf}(n) := [1 < n] \min\{p : p \mid n\} + [1 = n] n n=1 1 F_{\mathrm{prime}}(n) := \sum_{2 \le p \le n} f(p) F_{k}(n) := \sum_{i = 2}^{n} [p_{k} \le \operatorname{lpf}(i)] f(i)
具体方法¶
观察
考虑如何求出
最后一步推导基于这样一个事实:对于满足
其边界值即为
假设现在已经求出了所有的
- 直接按照递推式计算。
- 从大到小枚举
p p^{2} < n
现在考虑如何计算
观察求
一般情况下,
那么对于每个
分开考虑每个
Notice:
是完全积性函数! g(p) = p^{s}
于是设
对于一个合数
考虑
对于转移,考虑埃筛的过程,分开讨论每部分的贡献,有:
- 对于
n < p_{k}^{2} G G_{k}(n) = G_{k - 1}(n) - 对于
p_{k}^{2} \le n p_{k} -g(p_{k}) G_{k - 1}(n / p_{k}) - 对于第二部分,由于
p_{k}^{2} \le n \iff p_{k} \le n / p_{k} \operatorname{lpf}(i) < p_{k} i g(p_{k}) G_{k - 1}(p_{k - 1})
则有:
复杂度分析¶
对于
对于第二种方法,其本质即为洲阁筛的第二部分,在洲阁论文中也有提及(6.5.4),其时间复杂度被证明为
对于
考虑对于每个
对于空间复杂度,可以发现不论是
首先,通过一次数论分块可以得到所有的有效值,用一个大小为
然后分开考虑小于等于
这样,就可以使用两个大小为
有关代码实现¶
对于
对于
对于
相应地,
用 Extended Eratosthenes Sieve 求 积性函数
- 如何快速(一般是线性时间复杂度)筛出前
\sqrt{n} f f(p) - 如何快速求出
f(p^{c})
明确上述几点之后按顺序实现以下几部分即可:
- 筛出
[1, \sqrt{n}] \sqrt{n} f - 对
f(p) G F_{\mathrm{prime}} O(\sqrt{n}) - 按照
F_{k} F_{1}(n)
例题¶
求莫比乌斯函数的前缀和¶
求
易知
直接筛即可得到
求欧拉函数的前缀和¶
求
首先易知
对于
对于
筛两次加起来即可得到
「LOJ #6053」简单的函数¶
给定
易知
此处给出一种 C++ 实现:
参考代码
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | /* 「LOJ #6053」简单的函数 */
#include <algorithm>
#include <cmath>
#include <cstdio>
const int maxs = 200000; // 2sqrt(n)
const int mod = 1000000007;
template <typename x_t, typename y_t>
inline void inc(x_t &x, const y_t &y) {
x += y;
(mod <= x) && (x -= mod);
}
template <typename x_t, typename y_t>
inline void dec(x_t &x, const y_t &y) {
x -= y;
(x < 0) && (x += mod);
}
template <typename x_t, typename y_t>
inline int sum(const x_t &x, const y_t &y) {
return x + y < mod ? x + y : (x + y - mod);
}
template <typename x_t, typename y_t>
inline int sub(const x_t &x, const y_t &y) {
return x < y ? x - y + mod : (x - y);
}
template <typename _Tp>
inline int div2(const _Tp &x) {
return ((x & 1) ? x + mod : x) >> 1;
}
//以上目的均为防负数和取模
template <typename _Tp>
inline long long sqrll(const _Tp &x) { //平方函数
return (long long)x * x;
}
int pri[maxs / 7], lpf[maxs + 1], spri[maxs + 1], pcnt;
inline void sieve(const int &n) {
for (int i = 2; i <= n; ++i) {
if (lpf[i] == 0) { //记录质数
lpf[i] = ++pcnt;
pri[lpf[i]] = i;
spri[pcnt] = sum(spri[pcnt - 1], i); //前缀和
}
for (int j = 1, v; j <= lpf[i] && (v = i * pri[j]) <= n; ++j) lpf[v] = j;
}
}
long long global_n;
int lim;
int le[maxs + 1], // x <= \sqrt{n}
ge[maxs + 1]; // x > \sqrt{n}
#define idx(v) (v <= lim ? le[v] : ge[global_n / v])
int G[maxs + 1][2], Fprime[maxs + 1];
long long lis[maxs + 1];
int cnt;
inline void init(const long long &n) {
for (long long i = 1, j, v; i <= n; i = n / j + 1) {
j = n / i;
v = j % mod;
lis[++cnt] = j;
(j <= lim ? le[j] : ge[global_n / j]) = cnt;
G[cnt][0] = sub(v, 1ll);
G[cnt][1] = div2((long long)(v + 2ll) * (v - 1ll) % mod);
}
}
inline void calcFprime() {
for (int k = 1; k <= pcnt; ++k) {
const int p = pri[k];
const long long sqrp = sqrll(p);
for (int i = 1; lis[i] >= sqrp; ++i) {
const long long v = lis[i] / p;
const int id = idx(v);
dec(G[i][0], sub(G[id][0], k - 1));
dec(G[i][1], (long long)p * sub(G[id][1], spri[k - 1]) % mod);
}
}
/* F_prime = G_1 - G_0 */
for (int i = 1; i <= cnt; ++i) Fprime[i] = sub(G[i][1], G[i][0]);
}
inline int f_p(const int &p, const int &c) {
/* f(p^{c}) = p xor c */
return p xor c;
}
int F(const int &k, const long long &n) {
if (n < pri[k] || n <= 1) return 0;
const int id = idx(n);
long long ans = Fprime[id] - (spri[k - 1] - (k - 1));
if (k == 1) ans += 2;
for (int i = k; i <= pcnt && sqrll(pri[i]) <= n; ++i) {
long long pw = pri[i], pw2 = sqrll(pw);
for (int c = 1; pw2 <= n; ++c, pw = pw2, pw2 *= pri[i])
ans +=
((long long)f_p(pri[i], c) * F(i + 1, n / pw) + f_p(pri[i], c + 1)) %
mod;
}
return ans % mod;
}
int main() {
scanf("%lld", &global_n);
lim = sqrt(global_n); //上限
sieve(lim + 1000); //预处理
init(global_n);
calcFprime();
printf("%lld\n", (F(1, global_n) + 1ll + mod) % mod);
return 0;
}
|
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:Marcythm, Xeonacid
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用