loj#6235. 区间素数个数(洲阁筛)

4 篇文章 0 订阅
1 篇文章 0 订阅

题面在这里

之前写过一发…然后这次作为复习又重新写了一遍

然后发现比上一次快了2000+ms??尽管依然很慢 。我好像没加什么优化啊(

(许是loj评测机性能变佳…..。?

做法

洲阁筛模板。代码里有详细的注释。

代码

=> 注意初始化 //并不针对这道题,这题不初始化也没事因为只有一组数据,但是假如有多组或使用了多次cal的情况就要注意

#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x); i<=(y); i++)
#define per(i,x,y) for (int i=(x); i>=(y); i--)
#define ll long long
#define ld long double
#define inf 1000000000
using namespace std;
#define N 350005
int p[N/10],tot,res[N]; bitset<N> vis; ll n;
void pre(int n){
    rep (i,2,n){
        res[i]=res[i-1]; if (!vis[i]) p[++tot]=i,res[i]++;
        for (int j=1; j<=tot && (ll)i*p[j]<=n; j++){
            vis[i*p[j]]=1;
            if (i%p[j]==0) break;
        }
    }
}
#define M 350005
int sn,pos,cnt,last[M<<1]; ll g[M<<1],value[M<<1];
ll cal(ll n){//洲阁筛
    cnt=0; sn=(ll)sqrt((ld)n);//考虑<=n的数由<=sqrtn的质数筛出的情况
    pos=upper_bound(p+1,p+1+tot,sn)-p-1;//pos第一个小于等于sn的质数位置
    for (ll i=n; i>=1; i=n/(n/i+1)) value[++cnt]=n/i;//记录所有[n/i]的值,只有这样的数才会出现在转移中 //离散
    //g[i][j]表示1~j中与前i个质数互质的数的个数 //筛不掉的
    //g[i][j]=g[i-1][j]-g[i-1][j/p[i]]
    //当p[i+1]>j时,g[i][j]=1 //只有1
    //p[i]>j/p[i]时,g[i][j]=g[i-1][j]-1,可以O(1)计算
    ll k;
    rep (i,1,cnt) g[i]=value[i],last[i]=0;//注意初始化last[i]=0
    rep (i,1,pos) per (j,cnt,1){
        k=value[j]/p[i]; if (k<p[i]) break;//忽略那些-1的转移
        k=k<sn?k:cnt-n/k+1;//找到在value中的对应下标
        g[j]-=g[k]-(i-last[k]-1);//将g[k]的-1的转移补回去
        last[j]=i;
    }
    return res[sn]+g[cnt]-1;//-1是减去1的贡献
}
//#define local
int main(){
#ifdef local
    freopen("test.in","r",stdin); freopen("test.out","w",stdout);
#endif
    pre(350000); cin>>n; cout<<cal(n)<<endl;
    return 0;
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值