min_25筛学习笔记+牛客多校02E

本来没有学习这种较难的算法的想法的,因为比赛也做不到这种难度的题, 但是最近打牛客多校02,有一题要求 [1,n][1,n][1,n] 中素数的个数,我以为是像莫反一样容斥,但是后面感觉不行。赛后知道是用 min_25 筛来求,赛时过了一车,因此我也不得不学习这个算法了。

我打算拿洛谷里面的模板来举例。

就是给你积性函数
f(x)={kx=1g(x)=∑i=0kaixix=pc,p∈prime,c≥1f(x1)f(x2)(x1,x2)=1f(x)=\begin{cases} k & x=1 \\ g(x)=\sum_{i=0}^ka_ix^i & x=p^c,p\in prime,c\ge 1 \\ f(x_1)f(x_2) & (x_1,x_2)=1 \end{cases} f(x)=kg(x)=i=0kaixif(x1)f(x2)x=1x=pc,pprime,c1(x1,x2)=1

然后让你求
∑i=1nf(i)\sum_{i=1}^nf(i) i=1nf(i)

其中 n≤1013n\le10^{13}n1013

在牛客多校的那个题中,也有 n≤109n\le 10^9n109

一般来讲,想解决这种问题都要从 iii 最小的质因子入手,因为一个数要么是质数,要么最小的质因子 ≤n0.5\le n^{0.5}n0.5,而 n0.5n^{0.5}n0.5 一般都是10610^6106~10710^7107 数量级,可以直接用线性筛得到,也能全部枚举一次。因此总的思路大概就是先求出 ∑i=1ng(i)\sum_{i=1}^ng(i)i=1ng(i) 然后通过枚举最小的质因子来修正有超过一个质因子的数的贡献,这样能够很好的解决大质数无法直接得到的问题。

如果没有学过要怎么求 ∑i=1nik\sum_{i=1}^ni^ki=1nik,可以看我的博客

基于这个大的思路,我自己感觉 min_25 筛的思路是非常自然,很容易就能记下来。

对于式子
∑i=1nf(i)\sum_{i=1}^nf(i) i=1nf(i)

先将 111 去掉,因为其不包含任何素数,计算的时候容易出现奇怪的错误,虽然按照积性函数的定义, f(1)=1f(1)=1f(1)=1

于是变成
f(1)+∑i=2nf(i)f(1)+\sum_{i=2}^nf(i) f(1)+i=2nf(i)

自己感觉 min_25 筛很关键的一点是其能够快速求出
g(b)=∑p∈prime,p≤bpkg(b)=\sum_{p\in prime,p\le b}p^kg(b)=pprime,pbpk其中 b=⌊na⌋b=\left\lfloor \frac{n}{a} \right\rfloorb=an,这也意味着 bbb 的个数只有 O(n0.5)O(n^{0.5})O(n0.5)

虽然只能求出若干次方的和,但只要把每一项都求一次就可以得到原函数了。

然后剩下的和数就能通过枚举小最小的质因子来考虑

因此 min_25 筛有两步,分别是求出质数出的函数和,求出所有的函数和

第一步:求出质数处的函数和

这一步的思路就是像我刚才说的,先求出所有的函数和,然后通过枚举最小的质因子把和数的贡献剪掉即可。

我们从小到大枚举素数,并且同时维护所有的 g(b)g(b)g(b),假设当前的素数为 ppp,假设其为第 iii 个素数。那么显然有 p≤n0.5p\le n^{0.5}pn0.5。 我们记第 iii 个素数为 pip_ipi

对于 g(b)g(b)g(b),我们应该减去 111 ~ bbb 中最小质因子为 ppp 的和数,如果我们直接固定 ppp,也就是只需要考虑 111 ~ ⌊bp⌋\left\lfloor \frac{b}{p} \right\rfloorpb 中最小质因子 ≥p\ge pp 的数,然后你就会发现这个数刚好就是 g(⌊bp⌋)−∑j=1i−1pjkg(\left\lfloor \frac{b}{p} \right\rfloor)-\sum_{j=1}^{i-1}p_j^kg(pb)j=1i1pjk,(因为 ggg 函数中包含 <p<p<p 的素数)后者只需要线性筛的时候预处理即可。

然后容斥完以后我们就可以得到所有的 g(b)g(b)g(b) 了。

第二步:求出原函数

这一步的思路是也是拆分成质数贡献与和数贡献,质数贡献可以直接通过前面的 ggg 函数得到,和数贡献可以通过枚举最小质因子不断递归求解。

我们另 S(n,x)S(n,x)S(n,x) 表示 111 ~ nnn 中最小质因子 >px>p_x>px 的数的贡献之和,我们认为质数本身就是其最小质因子。

然后有
S(n,x)=g(n)−∑i=1xf(pi)+∑pic≤n,i>xf(pic)(S(⌊npic⌋,i+1)+[c>1])S(n,x)=g(n)-\sum_{i=1}^xf(p_i)+\sum_{p_i^c\le n,i>x}f(p_i^c)(S(\left\lfloor\frac{n}{p_i^c}\right\rfloor,i+1)+[c>1]) S(n,x)=g(n)i=1xf(pi)+picn,i>xf(pic)(S(picn,i+1)+[c>1])

因为 c>1c>1c>1 的时候 picp_i^cpic 本身也能够产生贡献。

以下是我洛谷模板的代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;const int mod = 1e9 + 7;int power(int a, int b) {int ret = 1;while (b) {if (b & 1) ret = 1ll * ret * a % mod;a = 1ll * a * a % mod; b >>= 1;}return ret;
}const int inv2 = power(2, mod - 2), inv3 = power(3, mod - 2);
const int N = 1e6 + 10;int cnt, p[N]; bool v[N];
int sp1[N], sp2[N];
ll n, Sqr, w[N];
int tot, g1[N], g2[N], ind1[N], ind2[N];void pre(int n) {v[1] = 1;for (int i = 2; i <= n; i++) {if (!v[i]) {p[++cnt] = i;sp1[cnt] = (sp1[cnt - 1] + i) % mod;sp2[cnt] = (sp2[cnt - 1] + 1ll * i * i) % mod;}for (int j = 1; j <= cnt && i * p[j] <= n; j++) {v[i * p[j]] = 1;if (i % p[j] == 0) break;}}
}int S(ll x, int y) {if (p[y] >= x) return 0;ll k = x <= Sqr ? ind1[x] : ind2[n / x];int ans = (1ll * g2[k] - g1[k] + mod - (sp2[y] - sp1[y]) + mod) % mod;for (int i = y + 1; i <= cnt && 1ll * p[i] * p[i] <= x; i++) {ll pe = p[i];for (int e = 1; pe <= x; e++, pe *= p[i]) {ll xx = pe % mod;ans = (ans + xx * (xx - 1) % mod * (S(x / pe, i) + (e != 1)) % mod) % mod;}}return ans % mod;
}int main() {// freopen("in.in", "r", stdin);ios::sync_with_stdio(false);cin.tie(nullptr);cin >> n;Sqr = sqrt((long double)n);pre(Sqr);for (ll i = 1, j; i <= n; i = j + 1) {j = n / (n / i);w[++tot] = n / i;int now = w[tot] % mod;g1[tot] = 1ll * now * (now + 1) / 2 % mod - 1;g2[tot] = 1ll * now * (now + 1) / 2 % mod * (2 * now + 1) % mod * inv3 % mod - 1;if (n / i <= Sqr) ind1[n / i] = tot;else ind2[n / (n / i)] = tot;}for (int i = 1; i <= cnt; i++) {for (int j = 1; j <= tot && 1ll * p[i] * p[i] <= w[j]; j++) {ll k = w[j] / p[i] <= Sqr ? ind1[w[j] / p[i]] : ind2[n / (w[j] / p[i])];g1[j] = (g1[j] - 1ll * p[i] * (g1[k] - sp1[i - 1] + mod) % mod + mod) % mod;g2[j] = (g2[j] - 1ll * p[i] * p[i] % mod * (g2[k] - sp2[i - 1] + mod) % mod + mod) % mod;}}cout << (S(n, 0) + 1) % mod << "\n";return 0;
}

时间复杂度

第一步的时间复杂度是 O(n0.75ln⁡n)O(\frac{n^{0.75}}{\ln n})O(lnnn0.75) 是没有争议的,有一些人底下写的是 log⁡\loglog 也是对的,因为只有常数倍的差别

对于第一重循环,因为素数的密度的是 1ln⁡n\frac{1}{\ln n}lnn1,所以可以提出一个 1ln⁡n\frac{1}{\ln n}lnn1

然后变为这个式子 O(∑i=1ni+ni)=O(∑i=1nni)=O(n∑i=1n1i)=O(n∫1n1xdx)=O(nn∣1n)=O(n0.75)O(\sum_{i=1}^{\sqrt n}\sqrt i+\sqrt \frac{n}{i})=O(\sum_{i=1}^{\sqrt n}\sqrt \frac{n}{i})=O(\sqrt n\sum_{i=1}^{\sqrt n}\sqrt \frac{1}{i})=O(\sqrt n\int_{1}^{n} \frac{1}{\sqrt x}\, dx)=O(\sqrt n\sqrt n|_{1}^{\sqrt n})=O(n^{0.75})O(i=1ni+in)=O(i=1nin)=O(ni=1ni1)=O(n1nx1dx)=O(nn1n)=O(n0.75)

第二步我在网上没有找到具体的证明,自己也感觉比较难证明,但是听说是一般情况是 O(n1−δ)O(n^{1-\delta})O(n1δ),但是如果 n≤1013n\le 10^{13}n1013,就满足 O(n0.75ln⁡n)O(\frac{n^{0.75}}{\ln n})O(lnnn0.75),基本适合OI的范围。

学完 min_25 筛,就会发现前面那个求素数个数的问题实在是太简单了,连 SSS 函数都不用算,直接把 ggg 函数计算以下就行了,然后把多项式设置为 f(x)=1f(x)=1f(x)=1 即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/89622.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/89622.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FunASR Paraformer-zh:高效中文端到端语音识别方案全解

项目简介 FunASR 是阿里巴巴达摩院开源的端到端语音识别工具箱,集成了多种语音识别、语音活动检测(VAD)、说话人识别等模块。其中 paraformer-zh 和 paraformer-zh-streaming 是针对中文语音识别任务优化的端到端模型,分别适用于离线和流式场景。Paraformer 采用并行 Tran…

数据结构自学Day9: 二叉树的遍历

一、二叉树的遍历“遍历”就是按某种规则 依次访问树中的每个节点&#xff0c;确保 每个节点都被访问一次且只访问一次遍历&#xff1a;前序 中序 后序&#xff08;深度优先&#xff09;&#xff0c;层序&#xff08;广度优先&#xff09;类型遍历方法特点深度优先遍历前序、中…

Leetcode(7.16)

求二叉树最小深度class Solution {public int minDepth(TreeNode root) {if (root null) {return 0;}Queue<TreeNode> queue new LinkedList<>();queue.offer(root);int depth 0;while (!queue.isEmpty()) {depth;int levelSize queue.size();for (int i 0; i…

Go从入门到精通(25) - 一个简单web项目-实现链路跟踪

Go从入门到精通(25) 一个简单web项目-实现链路跟踪 文章目录Go从入门到精通(25)前言为什么需要分布式链路跟踪&#xff1f;go实现链路跟踪搭建zipkin 服务安装依赖添加tracing包&#xff0c;OpenTelemetry 和Zipkin在 Gin 中集成 OpenTelemetry 中间件log包添加获取traceId方法…

2025年最新秋招java后端面试八股文+场景题

一、Java核心八股文&#xff08;2025年最新版&#xff09;1. Java基础HashMap vs ConcurrentHashMapHashMap&#xff1a;非线程安全&#xff0c;JDK1.8后采用数组链表/红黑树&#xff0c;扩容时可能死循环&#xff08;JDK1.7&#xff09;。ConcurrentHashMap&#xff1a;JDK1.8…

esp32 sd卡

ref&#xff1a; platform io & arduino Boards — PlatformIO latest documentation https://github.com/espressif/arduino-esp32/blob/master/libraries/SD_MMC/README.md SD 卡实验 | 极客侠GeeksMan GitHub - fabianoriccardi/ESPLogger: An Arduino library pro…

Java学习--------消息队列的重复消费、消失与顺序性的深度解析​

在 Java 分布式系统开发中&#xff0c;消息队列的应用已十分普遍。但随着业务规模扩大&#xff0c;消息的重复消费、意外消失、顺序错乱等问题逐渐成为系统稳定性的隐患。本文将从 Java 开发者的视角&#xff0c;深入分析这三大问题的产生原因、业务后果&#xff0c;并结合具体…

【Oracle】centos7离线静默安装oracle11g(p13390677_112040)

博文地址&#xff1a;https://blog.csdn.net/gitblog_06670/article/details/142569814 仓库地址&#xff1a;https://gitcode.com/Open-source-documentation-tutorial/31eb1/?utm_sourcedocument_gitcode&indexbottom&typecard 参考安装地址&#xff1a; 收费版&…

智能设备畅想

### 智能设备畅想 突然想到了一个好主意 因为最近在查无人机的相关资料&#xff08;很早之前就想搞个无人机玩玩但始终没有买&#xff09; 在了解自组装方面的内容时&#xff0c;和AI沟通了下 正好之前组装的 小智AI 基本上已经完善了&#xff0c;也正在考虑其在其他方向拓展的…

SpringAI——ChatModel

我的前面一篇文章&#xff08;SpringAI——ChatClient配置与使用&#xff09;中讲了ChatClient&#xff0c;它是一个构建于 ChatModel 之上的高层封装&#xff0c;它提供了更丰富的对话交互能力。可以这么说ChatModel相当于发动机&#xff0c;ChatClient相当于一台含有发动机、…

Zabbix监控K8S的PV信息详细教程!

文将介绍如何使用Zabbix自定义键值脚本方式监控K8S的PV卷状态等信息。 在Kubernetes (K8S) 中&#xff0c;PersistentVolume (PV) 是集群中的一个抽象层&#xff0c;它代表了底层存储资源&#xff0c;例如网络存储系统&#xff08;如NFS、Ceph、GlusterFS等&#xff09;或本地存…

wx小程序原生开发使用高德地图api

第一步&#xff1a;注册高德地图api的key第二步&#xff1a;下载amap-wx.js 放到项目的某个目录第三步&#xff1a;配置app.json&#xff08;必须&#xff0c;因为需要定位功能&#xff0c;&#xff09;"requiredPrivateInfos": ["getLocation"],"per…

如何通过mac的前24bit,模糊确认是那一台什么样的设备

MAC Address Lookup - MAC/OUI/IAB/IEEE Vendor Manufacturer Search Wireshark • Go Deep 上面这两个网址提供了&#xff0c;正对mac 的前24位&#xff0c;查找对应的网络设备厂商信息&#xff0c;可以让我们在运维过程中模糊的判断大约是什么型号的设备 使用macvendorloo…

【爬虫】04 - 高级数据存储

爬虫04 - 高级数据存储 文章目录爬虫04 - 高级数据存储一&#xff1a;加密数据的存储二&#xff1a;JSON Schema校验三&#xff1a;云原生NoSQL(了解)四&#xff1a;Redis Edge近端计算(了解)五&#xff1a;二进制存储1&#xff1a;Pickle2&#xff1a;Parquet一&#xff1a;加…

UDP和TCP的主要区别是什么?

在网络通信中&#xff0c;TCP&#xff08;传输控制协议&#xff09;和UDP&#xff08;用户数据报协议&#xff09;是两种核心的传输层协议。它们各自的特点和应用场景截然不同&#xff0c;理解两者的区别对于选择合适的通信方式至关重要。本文将通过几个关键点&#xff0c;用简…

Softhub软件下载站实战开发(十八):软件分类展示

Softhub软件下载站实战开发&#xff08;十八&#xff09;&#xff1a;软件分类展示 &#x1f5a5;️ 在之前文章中&#xff0c;我们实现了后台管理相关部分&#xff0c;本篇文章开始我们来实现用户端页面&#xff0c;由于内网使用&#xff0c;不需要sso优化等特性&#xff0c;我…

linux--------------------BlockQueue的生产者消费模型

1.基础BlockingQueue的生产者消费模型 1.1 BlockQueue 在多线程编程中阻塞队列是一种常用于实现生产者和消费者模型的数据结构&#xff0c;它与普通的队列区别在于&#xff0c;当队列为空时&#xff0c;从队列获取元素的操作将被阻塞&#xff0c;直到队列中放入了新的数据。当…

堆排序算法详解:原理、实现与C语言代码

堆排序&#xff08;Heap Sort&#xff09;是一种高效的排序算法&#xff0c;利用二叉堆数据结构实现。其核心思想是将待排序序列构造成一个大顶堆&#xff08;或小顶堆&#xff09;&#xff0c;通过反复调整堆结构完成排序。下面从原理到实现进行详细解析。一、核心概念&#x…

SSM框架——注入类型

引用类型的注入&#xff1a;Setter方法简单类型的注入&#xff1a;定义简单实例和方法在配置文件中对bean进行配置&#xff0c;使用porperty属性 值用value&#xff08;ref是用来获取bean的&#xff09;构造器方法&#xff1a;构造器方法中需要写name&#xff0c;这样程序就会耦…

信息学奥赛一本通 1552:【例 1】点的距离

【题目链接】 ybt 1552&#xff1a;【例 1】点的距离 【题目考点】 1. 最近公共祖先&#xff08;LCA&#xff09;&#xff1a;倍增求LCA 知识点讲解见&#xff1a;洛谷 P3379 【模板】最近公共祖先&#xff08;LCA&#xff09; 【解题思路】 首先用邻接表保存输入的无权图…