杜教筛学习笔记
杜教筛
杜教筛的基本形式
对于积性函数
对后面的
时间复杂度分析:
在求解过程中我们只需要关注
通过上面的分析我们可以知道杜教筛的时间复杂度是
如果能预处理出前
一些例子:
-
的前缀和,有
所以, -
的前缀和,有
所以,
贝尔级数
我们发现如果要用杜教筛求解积性函数前缀和问题,如何构造合适的狄利克雷卷积式子是很重要的一环,贝尔级数就是一个强有力的构造工具。
对于积性函数我们重点关注其在质数的幂处的取值,积性函数
然后数论函数的狄利克雷卷积就可以变成一般多项式的卷积:
一些常见积性函数的贝尔级数:
一些例子:
- 求积性函数
的前缀和。
Powerful Number
PN的定义是不含有次数为
这种形式的正整数一定可以写成
假设我们要求积性函数
此时必然存在一个积性函数
而根据
接下来只需要求解
考虑积性函数的取值只需要考虑其在质数的幂次处的取值:
由于是PN这里只需要考虑小于
例子:定义积性函数
构造
写出
注意到
对于
code:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define int long long
using namespace std;
const int mo = 1e9 + 7;
const int inv2 = (mo + 1) >> 1;
const int inv3 = (mo + 1) / 3;
const int inv6 = 1ll * inv2 * inv3 % mo;
int prime[200005], cnt = 0, g[2000005], n, sum[100005], ans = 0;
bool a[2000005];
vector<int> h[10005];
int mi(int o, int p) {
int yu = 1;
while (p) {
if (p & 1) yu = 1ll * yu * o % mo;
o = 1ll * o * o % mo;
p >>= 1;
}
return yu;
}
int S(int N) { return 1ll * N % mo * (N % mo + 1) % mo * inv2 % mo; }
int cal(int N) {
if (N <= 2000000) return g[N];
if (sum[n / N] != -1) return sum[n / N];
int yu = 1ll * N % mo * (N % mo + 1) % mo * ((N + N) % mo + 1) % mo;
yu = 1ll * yu * inv6 % mo;
for (int l = 2, r; l <= N; l = r + 1) {
r = N / (N / l);
yu -= 1ll * (S(r) - S(l - 1)) * cal(N / l) % mo;
yu = (yu % mo + mo) % mo;
}
return sum[n / N] = yu;
}
void dfs(int o, int p, int q) {
ans = (ans + 1ll * q * cal(n / o) % mo) % mo;
for (int tt = p; tt <= cnt; tt++) {
if (n / o < prime[tt] * prime[tt]) break;
int yu = o * prime[tt] * prime[tt];
for (int t = 2; yu <= n; yu *= prime[tt], t++) {
dfs(yu, tt + 1, 1ll * q * h[tt][t] % mo);
}
}
}
signed main() {
memset(sum, -1, sizeof(sum));
for (int i = 2; i <= 2000000; i++) {
if (!a[i]) {
prime[++cnt] = i;
g[i] = 1ll * i * (i - 1) % mo;
}
for (int j = 1; j <= cnt && i * prime[j] <= 2000000; j++) {
a[i * prime[j]] = 1;
if (i % prime[j] == 0) {
g[i * prime[j]] = 1ll * g[i] * prime[j] % mo * prime[j] % mo;
break;
}
g[i * prime[j]] = 1ll * g[i] * prime[j] % mo * (prime[j] - 1) % mo;
}
}
g[1] = 1;
for (int i = 2; i <= 2000000; i++) {
g[i] = (g[i - 1] + g[i]) % mo;
}
scanf("%lld", &n);
for (int i = 1; i <= cnt; i++) {
if (prime[i] > 100000) break;
h[i].push_back(1);
for (int j = prime[i], k = 1; j <= n; j *= prime[i], k++) {
int yu = 1ll * j % mo * (j % mo - 1) % mo;
for (int s = 0; s < k; s++) {
yu -= 1ll * h[i][s] * mi(prime[i], 2 * k - 2 * s - 1) % mo *
(prime[i] - 1) % mo;
yu = (yu % mo + mo) % mo;
}
h[i].push_back(yu);
}
}
dfs(1, 1, 1);
cout << ans << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示