zoukankan      html  css  js  c++  java
  • 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
    ", 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>
    #include <cmath>
    using namespace std;
    
    typedef long long ll;
    const int MAX_SQRT = 65536, MAX_N = 1000005;
    
    int lef, rig, tot_prime, ans;
    int prime[MAX_SQRT];
    bool not_prime[MAX_SQRT], GG[MAX_N];
    
    inline void get_prime(int top) {
        not_prime[1] = true;
        for (ll i = 2; i <= top; ++i) {
            if (!not_prime[i]) prime[++tot_prime] = i;
            for (int j = 1; j <= tot_prime && 1ll * i * prime[j] <= top; ++j) {
                not_prime[i * prime[j]] = true;
                if (i % prime[j] == 0) break;
            }
        }
        return;
    }
    
    int main() {
        get_prime(46341);
        scanf("%d%d", &lef, &rig);
        for (int i = 1; i <= tot_prime && prime[i] <= rig; ++i) {
            for (ll j = ll(ceil(double(lef) / prime[i])) * prime[i]; j <= rig; j += prime[i]) {
                if (j != prime[i]) GG[j - lef] = true;
            }
        }
        for (ll i = lef; i <= rig; ++i) ans += (!GG[i - lef]);
        printf("%d
    ", ans);
        return 0;
    }
    

      

  • 相关阅读:
    HDU 4814
    POJ 3415
    HDU 4941
    C scanf()
    hdu 4850 Wow! Such String!
    HDU 4828 Grids
    HDU 4832 Chess
    HDU 4831
    SpringCloud 网飞系 转换阿里系2
    用jianmu建木自动化打包vue前端应用,并远程ssh建立文件夹,scp文件至对应目录
  • 原文地址:https://www.cnblogs.com/xcysblog/p/9908652.html
Copyright © 2011-2022 走看看