loj#6235. 區間素數個數(洲閣篩)

題面在這裏

之前寫過一發…然後這次作爲複習又重新寫了一遍

然後發現比上一次快了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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章