Powerful Number 筛
定义¶
Powerful Number(以下简称 PN)筛类似于杜教筛,或者说是杜教筛的一个扩展,可以拿来求一些积性函数的前缀和。
要求:
- 存在一个函数
g g g - 对于质数
p g(p) = f(p)
假设现在要求积性函数
Powerful Number¶
定义:对于正整数
性质 1:所有 PN 都可以表示成
证明:若
性质 2:
证明:考虑枚举
那么如何求出
PN 筛¶
首先,构造出一个易求前缀和的积性函数
然后,构造函数
易得
对于素数
现在,根据
下面考虑计算
PN 筛的一般过程¶
- 构造
g - 构造快速计算
G - 计算
h(p^c) - 搜索 PN,过程中累加答案
- 得到结果
对于第 3 步,可以直接根据公式计算,可以使用枚举法预处理打表,也可以搜索到了再临时推。
复杂度分析¶
以使用第二种方法计算
对于第一部分,根据
对于搜索部分,由于
特别地,若借助杜教筛计算 map
记录较大的值,则杜教筛过程中用到的 map
中记录的,这一点可以直接用程序验证。
对于空间复杂度,其瓶颈在于存储
例题¶
「Luogu P5325」【模板】Min_25 筛¶
题意:给定积性函数
易得
考虑使用杜教筛求
之后
此外,此题还可以直接求出
再根据
参考代码
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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | #include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
template <typename T>
inline int mint(T x) {
x %= MOD;
if (x < 0) x += MOD;
return x;
}
inline int add(int x, int y) { return x + y >= MOD ? x + y - MOD : x + y; }
inline int mul(int x, int y) { return (long long)1 * x * y % MOD; }
inline int sub(int x, int y) { return x < y ? x - y + MOD : x - y; } //防止负数
inline int qp(int x, int y) {
int r = 1;
for (; y; y >>= 1) {
if (y & 1) r = mul(r, x);
x = mul(x, x);
}
return r;
}
inline int inv(int x) { return qp(x, MOD - 2); }
namespace PNS {
const int N = 2e6 + 5;
const int M = 35;
long long global_n;
int g[N], sg[N];
int h[N][M];
bool vis_h[N][M];
int ans;
int pcnt, prime[N], phi[N];
bool isp[N];
void sieve(int n) {
pcnt = 0;
for (int i = 2; i <= n; ++i) isp[i] = true; //判断质数数组
phi[1] = 1;
for (int i = 2; i <= n; ++i) {
if (isp[i]) {
++pcnt;
prime[pcnt] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= pcnt; ++j) { //筛去非质数
long long nxt = (long long)1 * i * prime[j];
if (nxt > n) break;
isp[nxt] = false;
if (i % prime[j] == 0) { // i是非质数的情况
phi[nxt] = phi[i] * prime[j];
break;
}
phi[nxt] = phi[i] * phi[prime[j]];
}
}
for (int i = 1; i <= n; ++i) g[i] = mul(i, phi[i]);
sg[0] = 0;
for (int i = 1; i <= n; ++i) sg[i] = add(sg[i - 1], g[i]); // g函数的前缀和
}
int inv2, inv6;
void init() {
sieve(N - 1);
for (int i = 1; i <= pcnt; ++i) h[i][0] = 1, h[i][1] = 0;
for (int i = 1; i <= pcnt; ++i) vis_h[i][0] = vis_h[i][1] = true;
inv2 = inv(2);
inv6 = inv(6);
}
int S1(long long n) { return mul(mul(mint(n), mint(n + 1)), inv2); }
int S2(long long n) {
return mul(mul(mint(n), mul(mint(n + 1), mint(n * 2 + 1))), inv6);
}
map<long long, int> mp_g;
int G(long long n) {
if (n < N) return sg[n];
if (mp_g.count(n)) return mp_g[n];
int ret = S2(n);
for (long long i = 2, j; i <= n; i = j + 1) {
j = n / (n / i);
ret = sub(ret, mul(sub(S1(j), S1(i - 1)), G(n / i)));
}
mp_g[n] = ret;
return ret;
}
void dfs(long long d, int hd, int pid) {
ans = add(ans, mul(hd, G(global_n / d)));
for (int i = pid, p; i <= pcnt; ++i) {
if (i > 1 && d > global_n / prime[i] / prime[i]) break; //剪枝
int c = 2;
for (long long x = d * prime[i] * prime[i]; x <= global_n;
x *= prime[i], ++c) { //计算f.g函数
if (!vis_h[i][c]) {
int f = qp(prime[i], c);
f = mul(f, sub(f, 1));
int g = mul(prime[i], prime[i] - 1);
int t = mul(prime[i], prime[i]);
for (int j = 1; j <= c; ++j) {
f = sub(f, mul(g, h[i][c - j]));
g = mul(g, t);
}
h[i][c] = f;
vis_h[i][c] = true;
}
if (h[i][c]) dfs(x, mul(hd, h[i][c]), i + 1);
}
}
}
int solve(long long n) {
global_n = n;
ans = 0;
dfs(1, 1, 1);
return ans;
}
} // namespace PNS
int main() {
PNS::init();
long long n;
scanf("%lld", &n);
printf("%d\n", PNS::solve(n));
return 0;
}
|
「LOJ #6053」简单的函数¶
给定
易得:
构造
易证
下面考虑求
记
当
当
综上,有
参考代码
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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | #include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int inv2 = (MOD + 1) / 2;
template <typename T>
inline int mint(T x) {
x %= MOD;
if (x < 0) x += MOD;
return x;
}
inline int add(int x, int y) {
return x + y >= MOD ? x + y - MOD : x + y;
} //防止大于模数
inline int mul(int x, int y) { return (long long)1 * x * y % MOD; }
inline int sub(int x, int y) { return x < y ? x - y + MOD : x - y; } //防负数
namespace PNS {
const int N = 2e6 + 5;
const int M = 35;
long long global_n;
int s1[N], s2[N];
int h[N][M];
bool vis_h[N][M];
int ans;
int pcnt, prime[N], phi[N];
bool isp[N];
void sieve(int n) {
pcnt = 0;
for (int i = 2; i <= n; ++i) isp[i] = true; //判断质数数组
phi[1] = 1;
for (int i = 2; i <= n; ++i) {
if (isp[i]) {
++pcnt;
prime[pcnt] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= pcnt; ++j) { //筛去非质数
long long nxt = (long long)1 * i * prime[j];
if (nxt > n) break;
isp[nxt] = false;
if (i % prime[j] == 0) { // i是非质数的情况
phi[nxt] = phi[i] * prime[j];
break;
}
phi[nxt] = phi[i] * phi[prime[j]];
}
}
s1[0] = 0;
for (int i = 1; i <= n; ++i) s1[i] = add(s1[i - 1], phi[i]);
s2[0] = 0;
for (int i = 1; i <= n / 2; ++i) {
s2[i] = add(s2[i - 1], phi[2 * i]);
}
}
void init() {
sieve(N - 1);
for (int i = 1; i <= pcnt; ++i) h[i][0] = 1;
for (int i = 1; i <= pcnt; ++i) vis_h[i][0] = true;
}
map<long long, int> mp_s1;
int S1(long long n) {
if (n < N) return s1[n];
if (mp_s1.count(n)) return mp_s1[n];
int ret = mul(mul(mint(n), mint(n + 1)), inv2);
for (long long i = 2, j; i <= n; i = j + 1) {
j = n / (n / i);
ret = sub(ret, mul(mint(j - i + 1), S1(n / i)));
}
mp_s1[n] = ret;
return ret;
}
map<long long, int> mp_s2;
int S2(long long n) {
if (n < N / 2) return s2[n];
if (mp_s2.count(n)) return mp_s2[n];
int ret = add(S1(n), S2(n / 2));
mp_s2[n] = ret;
return ret;
}
int G(long long n) { return add(S1(n), mul(2, S2(n / 2))); }
void dfs(long long d, int hd, int pid) {
ans = add(ans, mul(hd, G(global_n / d)));
for (int i = pid, p; i <= pcnt; ++i) {
if (i > 1 && d > global_n / prime[i] / prime[i]) break; //剪枝
int c = 2;
for (long long x = d * prime[i] * prime[i]; x <= global_n;
x *= prime[i], ++c) {
if (!vis_h[i][c]) {
int f = prime[i] ^ c, g = prime[i] - 1;
// p = 2时特判一下
if (i == 1) g = mul(g, 3);
for (int j = 1; j <= c; ++j) {
f = sub(f, mul(g, h[i][c - j]));
g = mul(g, prime[i]);
}
h[i][c] = f;
vis_h[i][c] = true;
}
if (h[i][c]) dfs(x, mul(hd, h[i][c]), i + 1);
}
}
}
int solve(long long n) {
global_n = n;
ans = 0;
dfs(1, 1, 1);
return ans;
}
} // namespace PNS
int main() {
PNS::init(); //预处理函数
long long n;
scanf("%lld", &n);
printf("%d\n", PNS::solve(n));
return 0;
}
|
习题¶
参考资料¶
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:OI-wiki
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用