1. 程式人生 > >Luogu1835 素數密度_NOI導刊2011提高(04

Luogu1835 素數密度_NOI導刊2011提高(04

這題有兩種做法,一種是 Miller-Rabin 直接暴力做

還有一種是正解的篩法


 先說 Miller-Rabin

就直接上板子就行了

但是 1e6 帶一堆 log 顯然不穩

就 “記憶化” 一下,把每個詢問過的數字的倍數直接處理掉

直接上真的會 T
直接把詢問過的數字倍數的抹掉就能過了
其實應該是把質數的倍數抹掉感覺能少一點常數

程式碼:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cstdio>
using namespace std;

typedef long long ll;
const int MAX_SIZ = 2000005;



int lef, rig, ans;
int prime[3] = {2, 7, 61};
bool not_prime[MAX_SIZ];

inline int fast_pow(int bot, int top, int mod) {
    register int ret = 1;
    while (top) {
        if (top & 1) ret = 1ll * ret * bot % mod;
        top >>= 1;
        bot = 1ll * bot * bot % mod;
    }
    return ret;
}
inline bool dvd_chk(int bot, int top, int mod) {
    register int tmp;
    while (!(top & 1)) {
        tmp = fast_pow(bot, top, mod);
        if (tmp == 1) top >>= 1;
        else if (tmp == mod - 1) return true;
        else return false;
    }
    return true;
}
inline bool Test(int a) {
    if (a <= 1) return false;
    if (a == 2) return true;
    if (!(a & 1)) return false;
    for (int i = 0; i < 3; ++i) {
        if (prime[i] == a) return true;
        if (fast_pow(prime[i], a - 1, a) != 1) return false;
        if (!dvd_chk(prime[i], a - 1, a)) return false;
    }
    return true;
}

int main() {
    scanf("%d%d", &lef, &rig);
    register ll tmp;
    for (ll i = lef; i <= rig; ++i) {
        if (not_prime[i - lef] || i == 1) continue;
        if (Test(i)) {
            ++ans;
	        tmp = (2ll * i);
	        while (tmp <= rig) {
	            not_prime[tmp - lef] = true;
	            tmp += tmp;
	        }
        } else not_prime[i - lef] = true;
    }
    printf("%d\n", ans);
    return 0;
}

 然後是正解的做法

直接線性篩肯定是不可行的

考慮怎麼搞掉特別大的合數

2147483647 的 mindiv 也不過才 46341

所以直接用 mindiv 去刪掉區間內的數就行了,其他的數不用管

這樣它大概是個 len * ln len 的

由於這並不是調和級數,分母都是質數,而且並不是 1e6 內的所有
所以它還要小很多

總之比 n log n 快

程式碼:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cstdio>
using namespace std;

typedef long long ll;
const int MAX_SIZ = 2000005;



int lef, rig, ans;
int prime[3] = {2, 7, 61};
bool not_prime[MAX_SIZ];

inline int fast_pow(int bot, int top, int mod) {
    register int ret = 1;
    while (top) {
        if (top & 1) ret = 1ll * ret * bot % mod;
        top >>= 1;
        bot = 1ll * bot * bot % mod;
    }
    return ret;
}
inline bool dvd_chk(int bot, int top, int mod) {
    register int tmp;
    while (!(top & 1)) {
        tmp = fast_pow(bot, top, mod);
        if (tmp == 1) top >>= 1;
        else if (tmp == mod - 1) return true;
        else return false;
    }
    return true;
}
inline bool Test(int a) {
    if (a <= 1) return false;
    if (a == 2) return true;
    if (!(a & 1)) return false;
    for (int i = 0; i < 3; ++i) {
        if (prime[i] == a) return true;
        if (fast_pow(prime[i], a - 1, a) != 1) return false;
        if (!dvd_chk(prime[i], a - 1, a)) return false;
    }
    return true;
}

int main() {
    scanf("%d%d", &lef, &rig);
    register ll tmp;
    for (ll i = lef; i <= rig; ++i) {
        if (not_prime[i - lef] || i == 1) continue;
        if (Test(i)) {
            ++ans;
            tmp = (2ll * i);
            while (tmp <= rig) {
                not_prime[tmp - lef] = true;
                tmp += tmp;
            }
        } else not_prime[i - lef] = true;
    }
    printf("%d\n", ans);
    return 0;
}