这题有两种做法,一种是 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>
#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\n", ans);
return 0;
}