问题描述

给定整数 nnn,求[1,n][1,n][1,n]中所有素数的和,答案对998244353取模。

数据范围

1s,512M

对于 100%100\%100% 的数据,n≤1010n\leq 10^{10}n≤1010

解题思路

回忆素数的欧拉筛法(Euler’s_sieve)。

  1. 对于当前数 xxx,若未被判定为合数,即为素数,将其插入素数列表末尾。

  2. 将 xxx 与素数列表中的所有数从小到大相乘,并将乘积判定为合数。若 xxx 为当前素数的倍数,则不再与下一个素数相乘。

因此当一个数被判定为合数时,必然是通过其最小素因数筛出。

证明如下:

设 y=p1a1×p2a2×⋯×pnany=p_1^{a_1}\times p_2^{a_2}\times\cdots\times p_n^{a_n}y=p1a1​​×p2a2​​×⋯×pnan​​,其中 p1<p2<⋯<pnp_1 < p_2 < \cdots < p_np1​<p2​<⋯<pn​,pip_ipi​ 为素数。

若 yyy 通过 pip_ipi​ 筛出,则 y=pi×(p1a1×p2a2×⋯×piai−1⋯×pnan)y=p_i \times (p_1^{a_1}\times p_2^{a_2}\times\cdots\times p_i^{a_i-1}\cdots \times p_n^{a_n})y=pi​×(p1a1​​×p2a2​​×⋯×piai​−1​⋯×pnan​​),而 p1a1×p2a2×⋯×piai−1⋯×pnanp_1^{a_1}\times p_2^{a_2}\times\cdots\times p_i^{a_i-1}\cdots \times p_n^{a_n}p1a1​​×p2a2​​×⋯×piai​−1​⋯×pnan​​ 为 p1p_1p1​ 的倍数,因而在 p1p_1p1​ 筛完后操作终止,不可能继续到 pip_ipi​。

定义 S(x,y)S(x,y)S(x,y),表示在 [2,x][2,x][2,x]中,通过所有不大于 yyy 的质数筛完后依然未被判定为合数的数之和。

定于 T(x,y)T(x,y)T(x,y),表示满足 S(x,y)S(x,y)S(x,y) 要求的所有数的集合。

根据上文分析,得到关于 T(x,y)T(x,y)T(x,y) 的性质:属于 T(x,y)T(x,y)T(x,y) 的数要么是素数,要么其最小素因子大于 yyy。

那么问题所求的值为 S(n,⌊n⌋)mod998244353S(n, \lfloor\sqrt{n}\rfloor) \bmod 998244353S(n,⌊n​⌋)mod998244353。

可以通过状态转移方程求解。

初始值 S(x,1)=∑i=2xi=(x+2)×(x−1)2S(x,1)=\sum\limits_{i=2}^{x}{i}=\frac{(x+2)\times (x-1)}{2}S(x,1)=i=2∑x​i=2(x+2)×(x−1)​,此时未被任何素数筛选,所有数都符合要求。

若 yyy 为合数,则 S(x,y)=S(x,y−1)S(x,y)=S(x,y-1)S(x,y)=S(x,y−1)。因为素数列表和筛选范围都没有发生变化。

若 yyy 为素数,且 y2>xy^2 > xy2>x,则 S(x,y)=S(x,y−1)S(x,y)=S(x,y-1)S(x,y)=S(x,y−1)。因为任何数 ppp 的因数都不大于 p\sqrt{p}p​,而 y>xy > \sqrt{x}y>x​,因此 yyy 不可能是 [2,x][2,x][2,x] 中任何数的最小素因数。

现在讨论最后一种情况,若 yyy 为素数,且 y2≤xy^2 \leq xy2≤x。

考虑 S(x,y)S(x,y)S(x,y) 相比于 S(x,y−1)S(x,y-1)S(x,y−1) 多筛去了哪些数,即 [2,x][2,x][2,x] 中所有最小素因数为 yyy 的数。

这些数可以表示为 y×ziy\times z_iy×zi​,其中 zi∈[2,⌊xy⌋]z_i\in [2,\lfloor\frac{x}{y}\rfloor]zi​∈[2,⌊yx​⌋],且 ziz_izi​ 的最小素因数不小于 yyy。

而根据 T(x,y)T(x,y)T(x,y) 的性质, T(⌊xy⌋,y−1)T(\lfloor\frac{x}{y}\rfloor,y-1)T(⌊yx​⌋,y−1) 为所有最小素因数大于 y−1y-1y−1 或其本身为素数的数的集合。

因而 zi∈T(⌊xy⌋,y−1)z_i\in T(\lfloor\frac{x}{y}\rfloor,y-1)zi​∈T(⌊yx​⌋,y−1),且 {zi}+{prime≤y−1}=T(⌊xy⌋,y−1)\{z_i\} + \{prime \leq y-1\} = T(\lfloor\frac{x}{y}\rfloor,y-1){zi​}+{prime≤y−1}=T(⌊yx​⌋,y−1),而 ∑prime≤y−1prime\sum\limits_{prime \leq y-1}{prime}prime≤y−1∑​prime 可以表示为 S(y−1,y−1)S(y-1,y-1)S(y−1,y−1)。

得到 S(x,y)=S(x,y−1)−y×(S(⌊xy⌋,y−1)−S(y−1,y−1))S(x,y)=S(x,y-1)-y\times (S(\lfloor\frac{x}{y}\rfloor,y-1)-S(y-1,y-1))S(x,y)=S(x,y−1)−y×(S(⌊yx​⌋,y−1)−S(y−1,y−1))。

参考代码

C++11:

#include <bits/stdc++.h>
#include <bits/extc++.h>typedef long long ll;
const ll MOD = 998244353;ll N;
std::vector<ll> A;
__gnu_pbds::cc_hash_table<ll, ll> S;int main() {scanf("%d", &N);ll r = sqrt(N);for (ll i = 1; i <= r; ++i)A.push_back(N / i);for (ll i = A.back() - 1; i >= 1; --i)A.push_back(i);for (auto i : A) S[i] = (i + 2) % MOD * ((i - 1) % MOD) / 2;for (ll p = 2; p <= r; ++p) {if (S[p] != S[p - 1]) {ll sp = S[p - 1];ll pp = p * p;for (auto i : A) {if (i < pp) break;S[i] = ((S[i] - p * (S[i / p] - sp)) % MOD + MOD) % MOD;}}}printf("%lld\n", S[N]);return 0;
}

python3:

def Solve(n):r = int(n ** 0.5)V = [n // i for i in range(1, r + 1)]V += list(range(V[-1] - 1, 0, -1))S = {i : i * (i + 1) // 2 - 1 for i in V}tms = 0for p in range(2, r + 1):if S[p] != S[p - 1]:sp = S[p - 1]p2 = p * pfor v in V:if v < p2: breakS[v] -= p * (S[v // p] - sp)return S[n]if __name__ == '__main__':print(Solve(int(input())) % 998244353)

注意事项

注意到LLONG_MAX=9223372036854775807,即不超过 101910^{19}1019,因而两个 101010^{10}1010 级别的数相乘可能溢出,需要先取模再相乘。

使用cc_hash_table 而不是map 的原因在于前者是哈希表,定位复杂度为 O(1)O(1)O(1)。而通过测试发现同为哈希表的 gp_hash_tableunordered_map 的表现都很不尽如人意,耗时至少5倍以上。

复杂度分析

实测大约是 O(n0.75)O(n^{0.75})O(n0.75) 的量级,但是不太会计算具体值。

参考链接

Project Euler-Problem 10 Lucy_Hedgehog

知乎-求十亿内所有质数的和,怎么做最快? 菜鱼ftfish

求[1,n]中所有素数之和相关推荐

  1. 用子函数的方法求一维数组中所有元素之和

    <程序设计基础实训指导教程-c语言> ISBN 978-7-03-032846-5 p142 7.1.2 上级实训内容 [实训内容2]用子函数的方法求一维数组中所有元素之和 #includ ...

  2. c语言 do while 素数,1. 编写程序,求30以内的所有素数之和.用do while做

    用JAVA编写一个程序,求1000以内所有偶数的和以及奇数的和,并将偶数和以及奇数和输出到屏幕上. publicclassTest{publicvoiddisplay(){intsum=0;for(i ...

  3. js求(1~n)之间 素数之和

    现在给你一个整数 N(2<N<1000),代表警员的编号,现在要 求你写出一个程序,求出从 1~N 个数中的所有素数的和,该和为警 员对应部门的编号. 例如输入:3 输出 1~3 的素数{ ...

  4. 爬虫练手——求一个网页中所有数字之和!(题网:http://www.glidedsky.com/)

    第一道:题目如下! 1.目标URL:http://www.glidedsky.com/,此网站中的第一题. 2.python中requests+etree+xpath实现:(cookie_str数据大 ...

  5. 枚举算法:概率计算。在标注编号分别为1,2,...,n的n张牌中抽取3张,试求抽出3张牌编号之和为素数的概率。输入整数n(3<n<=3000),输出对应的概率(四舍五入到小数点后第3位)。

    概率计算.在标注编号分别为1,2,-,n的n张牌中抽取3张,试求抽出3张牌编号之和为素数的概率.输入整数n(3<n<=3000),输出对应的概率(四舍五入到小数点后第3位). 思路: 组合 ...

  6. MATLAB编程经典程序 素数的判断,求0~100素数之和

    clear sum=5;         %求0-100素数之和 ss=0;          %用来标定是否是素数,0表示不是 prime=[2 3];     %用来存放素数,2,3为素数,先放置 ...

  7. 利用python求100以内素数之和

    利用python求100以内素数之和 python中简单函数的应用 #Prime def is_prime(n): #定义一个判断素数的函数for i in range(2,n): #判断是否能被除1 ...

  8. 2、求100以内的素数之和。(20分)

    题目: /* 2.求100以内的素数之和.(20分) */ 代码: public class Two207 {public static void main(String[] args) {int s ...

  9. 【c语言】输入一个4位数,求四位数中各位数相加之和

    <程序设计基础实训指导教程-c语言>杨莉 龚义建 科学出版社 ISBN 978-7-03-032846-5 p9 2.1.2 上机实训内容 [实训内容1] 编程实现:输入一个4位数,求4位 ...

最新文章

  1. 树莓派的Raspbian Stretch with desktop和Ubuntu Mate(废弃)
  2. ICCV 2021 Workshop 盘点
  3. c#是否参入中间变量交换变量的几种方法
  4. Vista SP1、IIS7,安装ASP.Net 1.1、VS2003、NetAdvantage 2004vol、Sql Server2000全攻略
  5. 支付宝上线长辈模式: 字体图标加大 去除了营销推送
  6. mysql gtid 双主_MySQL5.7配置GTID双主
  7. 自定义可拖拽GridView控件
  8. 计算机职业英语一级是什么,计算机职业英语一级.doc
  9. 电脑文件删除不掉什么原因?程序显示被占用清理不掉如何操作?
  10. 关于教程被人盗版出售的一些感想
  11. 安卓系统加速_真就这么简单让你的安卓手机变流畅?
  12. java到达时间后自动执行代码_java设置按时间自动执行
  13. 【Matplotlib设置】Python绘图全局字体改为 Times New Roman
  14. 都市白领要学会的规则
  15. 用Python分析了30000+《独行月球》影评数据,看看观众们怎么说~
  16. 微信定位和HTML5定位
  17. 深度学习之 imgaug (图像增强)学习笔记
  18. Object的notify和notifyAll方法的区别
  19. 搜索引擎技术 ——链接分析
  20. P1823 [COI2007] Patrik 音乐会的等待(单调栈)

热门文章

  1. 视频转图片序列 java_OpenCV 视频与图片序列相互转换
  2. python图像rgb转灰度
  3. xwiki的搭建及jetty升级
  4. 学校计算机安全隐患排查情况报告,学校安全隐患排查情况报告
  5. Ptcms在php7.2.10下的采集调试
  6. BetaFlight深入传感设计之五:MahonyAHRS 方向余弦矩阵理论
  7. 扫描版PDF添加目录
  8. python快手虎年跳一跳辅助(点击版)
  9. 医学图像切片的三维重建matlab仿真
  10. MPI数据通信常用函数