这题有两种做法,一种是 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; }