Loading

题解:P7884 【模板】Meissel-Lehmer

前言

这篇题解可能并不是很正经,作者并不会分析时间复杂度,有人会吗?

其实是暴力出奇迹

这个做法是我在翻找 我一年前写的博客想到整出来的,跪求大佬优化那篇博客那个算法。

做法

注:前面这一段和 之前的博客 相同。

回顾埃氏筛的过程,对于每个质数 \(p\),筛掉 \(p\) 的倍数。

定义函数 \(S(v,p)\) 表示用不超过 \(p\) 的质数筛 \(1 \sim v\) 后剩余的数的个数(除去 \(1\))。

比如 \(S(10,3)\)\(1 \sim 10\) 内,通过 \(2\) 筛去 \(4,6,8,10\),通过 \(3\) 筛去 \(6,9\),除去 \(1\) 剩余 \(2,3,5,7\)。因此 \(S(10,3)=4\).

对于 \(S(v,p)\)

  • \(p\) 不是质数,\(S(v,p)=S(v,p-1)\)

  • \(p*p>v\)\(p\) 不可能再筛任何数,\(S(v,p)=S(v,p-1)\)

  • \(p\) 是质数且 \(p*p \le v\),假设 \(x=p \times k\) 能被筛掉,则 \(p \le k \le v/p\)\(k\) 不被不超过 \(p-1\) 的质数整除,这样的 \(k\) 共有 \(S(v/p,p-1)-S(p-1,p-1)\) 个。

于是我们发现一个重要的式子:

\(S(v,p)=S(v,p-1)-(S(v/p,p-1)-S(p-1,p-1))\)\(p\) 是质数且 \(p \le \sqrt{v}\)


到了这一步我们不再沿用之前的 DP 做法,我们直接暴力递归 \(S\) 函数,但这显然会超时。考虑预处理优化。

我们发现当 \(p^2>v\)\(S(v,p)=\pi(v)\),考虑预处理一部分的 \(\pi(v)\),这里我预处理了 \(v\le5\times10^7\)\(\pi(v)\)

然后我们可以预处理一部分比较小的 \(S(v,p)\),这里我预处理了 \(p\le10^3,v\le 2.5\times10^5\)\(S(v,p)\)

然后就过了???

Code

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<string>
#include<bitset>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=5e7+10;
const int N=5e7;
const int INF=0x3f3f3f3f;
const long long LINF=0x3f3f3f3f3f3f3f3f;
int cnt=0;
int f[MAXN];
int pr[3001145];
bitset <MAXN> unp;
const int MAXM=2.5e5+10;
const int M=2.5e5;
int tot=0;
int g[170][MAXM];
inline void seive(){
    unp[0]=true;
    unp[1]=true;
    for(int i=2;i*i<=N;i++)
    {
        if(unp[i]){
            continue;
        }
        for(int j=i*i;j<=N;j+=i)
        {
            unp[j]=true;
        }
    }
    for(int i=1;i<=N;i++)
    {
        f[i]=f[i-1];
        if(!unp[i]){
            cnt++;
            pr[cnt]=i;
            f[i]++;
        }
    }
    unp.reset();
    unp[0]=true;
    unp[1]=true;
    for(int i=2;i<=1000;i++)
    {
        if(unp[i]){
            continue;
        }
        tot++;
        for(int j=i*i;j<=M;j+=i)
        {
            unp[j]=true;
        }
        g[tot][0]=0;
        for(int j=1;j<=M;j++)
        {
            g[tot][j]=g[tot][j-1]+!unp[j];
        }
    }
}
long long S(long long n,long long p){
    if(!p){
        return n-1;
    }
    if(n<1ll*pr[p]*pr[p]){
        if(n<=N){
            return f[n];
        }
        else{
            return S(n,p-1);
        }
    }
    if(n<=M){
        return g[p][n];
    }
    return S(n,p-1)-(S(n/pr[p],p-1)-S(pr[p]-1,p-1));
}
signed main(){
    seive();
    long long n;
    scanf("%lld",&n);
    printf("%lld\n",S(n,cnt));
    return 0;
}
posted @ 2025-02-09 07:15  Mathew_Miao  阅读(41)  评论(0)    收藏  举报