51nod 1184 第n个素数

这题大概有两种解法:

1.预先打表放到数组里,大概100万间隔的素数的值$p_n$,然后用区间筛法。可以分的再细一点。

2.先估计,再用筛法。你需要一个第n个素数的估计值,一个素数统计函数和区间筛法,根据wiki上的关于第n个素数的公式,通过打表,我发现他有时大于实际值,有时小于实际值,我觉得让估计值保持小于实际值比较方便,这样区间筛的时候,只要向上筛就好了。经过修正我预估的第n个素数

$ p_n=n (n \log +\log  (n \log )-1)+\frac{n (\log  (n \log )-2)}{\log }-6*n/1000 $

我加入了一个向左的移动,通过打表验证,它一定会比实际的pn小,最大误差也在3e6左右。求出预估值以内有多少素数$\pi \left(p_n\right)$,记下与目标的差值,然后向上区间筛。

素数计数函数$\pi \left(p_n\right)$的算法来源于project euler problem 10Forum中第5页Lucy_Hedgehog的python代码,原理基本与wiki百科中的方法类似,虽然problem10是求素数的和,但简单修改2处之后就会变成素数统计函数,下文代码中会有注释。区间筛事先预处理出16万以内的素数就够了。

  1 #include<iostream>
  2 #include<cmath>
  3 #include<assert.h>
  4 using namespace std;
  5 typedef long long LINT;
  6 LINT a,b,goal,n;
  7 int mark[160000],prime[160000],e,bl[10000005];
  8 LINT pn(int n)
  9 {
 10     LINT s=LINT(n*(log(n)+log(log(n))-1)+n*(log(log(n))-2)/log(n)-6.0*n/1000.0);
 11     return s<=1?1:s;
 12 }
 13
 14 inline LINT V2IDX(LINT v, LINT N, LINT Ndr, LINT nv) {
 15     return v >= Ndr ? (N/v - 1) : (nv - v);
 16 }
 17
 18 LINT primesum(LINT N) {
 19     LINT *S;
 20     LINT *V;
 21
 22     LINT r = (LINT)sqrt(N);
 23     LINT Ndr = N/r;
 24
 25     assert(r*r <= N and (r+1)*(r+1) > N);
 26
 27     LINT nv = r + Ndr - 1;
 28
 29     V = new LINT[nv];
 30     S = new LINT[nv];
 31
 32     for (LINT i=0; i<r; i++) {
 33         V[i] = N/(i+1);
 34     }
 35     for (LINT i=r; i<nv; i++) {
 36         V[i] = V[i-1] - 1;
 37
 38     }
 39
 40     for (LINT i=0; i<nv; i++) {
 41       //S[i] = V[i] * (V[i] + 1) / 2 - 1;若求素数和,使用此处
 42         S[i]=V[i] - 1;
 43       //若求素数个数使用此处
 44     }
 45
 46     for (LINT p=2; p<=r; p++) {
 47         if (S[nv-p] > S[nv-p+1]) {
 48             LINT sp = S[nv-p+1];
 49             LINT p2 = p*p;
 50             for (LINT i=0; i<nv; i++) {
 51                 if (V[i] >= p2) {
 52                 //S[i] -= p* (S[V2IDX(V[i]/p, N, Ndr, nv)] - sp);若求素数和,使用此处
 53                     S[i] -= 1* (S[V2IDX(V[i]/p, N, Ndr, nv)] - sp);
 54                   //若求素数个数,使用此处
 55                 } else {
 56                     break;
 57                 }
 58             }
 59         }
 60     }
 61
 62     return S[0];
 63 }
 64
 65 int main()
 66 {
 67     cin>>n;
 68     a=pn(n);
 69     if(a%2&&a>1)a=a-1;//防止预估值本身就是素数的情况,刚开始被这里坑了3个test
 70     b=a+7000000;
 71     goal=n-primesum(a);
 72     for(int i=2;i<=160000;i++)
 73     {
 74         if(!mark[i])
 75         {
 76             prime[++e]=i;
 77             for(LINT j=(LINT)i*i;j<=160000;j+=i)
 78             {
 79                 mark[j]=1;
 80             }
 81         }
 82     }
 83     LINT xxx,c;
 84     for(int i=1;i<=e;i++)
 85     {
 86         xxx=(LINT)ceil(1.0*a/prime[i]);
 87         if(xxx==1) xxx++;
 88         for(LINT j=xxx;(c=j*prime[i])<b;j++)
 89         {
 90             bl[c-a]=1;
 91         }
 92     }
 93     int ans=0;
 94     c=b-a;
 95     if(a==1) ans--;
 96     for(int i=0;i<c;i++)
 97     {
 98         if(!bl[i]) ans++;
 99         if(ans==goal)
100         {
101             cout<<i+a<<endl;
102             break;
103         }
104     }
105     return 0;
106 }

时间: 2024-10-13 16:18:44

51nod 1184 第n个素数的相关文章

51NOD 1434 区间LCM(素数筛)

传送门 1434 区间LCM 题目来源: TopCoder 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 收藏 关注 一个整数序列S的LCM(最小公倍数)是指最小的正整数X使得它是序列S中所有元素的倍数,那么LCM(S)=X. 例如,LCM(2)=2,LCM(4,6)=12,LCM(1,2,3,4,5)=60. 现在给定一个整数N(1<=N<=1000000),需要找到一个整数M,满足M>N,同时LCM(1,2,3,4,-,N-1,N) 整除 LCM

【算法总结】积性函数相关

[线性筛] [模板代码] [线性筛质数] 1 int main() 2 { 3 n=read(); 4 for(int i=2;i<=n;i++) 5 { 6 if(!f[i])pri[++cnt]=i; 7 for(int j=1;j<=cnt;j++) 8 { 9 if(i*pri[j]>n)break; 10 f[i*pri[j]]=1; 11 if(i%pri[j]==0)break; 12 } 13 } 14 } [线性求约数和] 1 void pre() 2 { 3 miu[

51nod 1106 质数检测(miller rabin 素数测试.)

1106 质数检测 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题  收藏  关注 给出N个正整数,检测每个数是否为质数.如果是,输出"Yes",否则输出"No". Input 第1行:一个数N,表示正整数的数量.(1 <= N <= 1000) 第2 - N + 1行:每行1个数(2 <= S[i] <= 10^9) Output 输出共N行,每行为 Yes 或 No. Input示例 5 2 3 4 5 6

51nod 1060 最复杂的数(数论,反素数)

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1060 题解:可以去学习一下反素数. #include <iostream> #include <cstring> #define inf 1000000000000000007 using namespace std; typedef unsigned long long ull; const int M = 1e6 + 10; ull n ,

51Nod 1135-原根(快速求解一个素数的原根)

题目地址:51Nod 1135 1.原根定义:设m>1,gcd(a,m)=1,使得成立的最小的r,称为a对模m的阶. 2.定理:如果模m有原根,那么他一共有个原根. 3.定理:如果p为素数,那么素数p一定存在原根,并且模p的原根的个数为个. 4.定理:假设m是正整数,a是整数,如果a模m的阶等于,则称a为模m的一个原根. 5.模m有原根的充要条件:m=2,4,P^a,2*P^a--. 求模素数P的原根的方法:对P-1素因子分解,即P-1=(P1^a1)(P2^a2)-..(Pk^ak).,若恒有

51nod 1434 区间LCM 素数

1434 区间LCM 题目来源: TopCoder 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 一个整数序列S的LCM(最小公倍数)是指最小的正整数X使得它是序列S中所有元素的倍数,那么LCM(S)=X. 例如,LCM(2)=2,LCM(4,6)=12,LCM(1,2,3,4,5)=60. 现 在给定一个整数N(1<=N<=1000000),需要找到一个整数M,满足M>N,同时LCM(1,2,3,4,...,N- 1,N) 整除 LCM(N+1,N

51nod 1060 最复杂的数 反素数

1060 最复杂的数 基准时间限制:1 秒 空间限制:131072 KB 把一个数的约数个数定义为该数的复杂程度,给出一个n,求1-n中复杂程度最高的那个数. 例如:12的约数为:1 2 3 4 6 12,共6个数,所以12的复杂程度是6.如果有多个数复杂度相等,输出最小的. Input 第1行:一个数T,表示后面用作输入测试的数的数量.(1 <= T <= 100) 第2 - T + 1行:T个数,表示需要计算的n.(1 <= n <= 10^18) Output 共T行,每行2

51nod 1441 士兵的数字游戏 (素数处理

1441 士兵的数字游戏 题目来源: CodeForces 基准时间限制:6 秒 空间限制:131072 KB 分值: 40 难度:4级算法题  收藏  关注 两个士兵正在玩一个游戏,游戏开始的时候,第一个士兵为第二个士兵选一个正整数n.然后第二个士兵要玩尽可能多的轮数.每一轮要选择一个正整数x>1,且n要是x的倍数,然后用n/x去代替n.当n变成1的时候,游戏就结束了,第二个士兵所得的分数就是他玩游戏的轮数. 为了使游戏更加有趣,第一个士兵用 a! / b! 来表示n.k!表示把所有1到k的数

51nod:1689 逛街

原题链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1689 一开始想枚举逛街的终点,然后开两个大根堆维护b值,分别保证喜爱店不少于k,以及在此前提下逛到的店尽可能多.具体实现是维持第一个堆元素数为k,每加入一个元素,如果c为1,就丢进第一个堆,并把堆顶丢到第二个,c为0直接丢进第二个.如果两个堆的元素b值之和超过T,就从第二个堆删元素. 但是这种写法最后的删元素部分有可能删去对答案有贡献的点,这时再把丢掉的元素