果然SPOJ第二题就很有趣。
因为n太大,线性筛和普通判断方法都不行。于是要用区间筛。
先预处理出sqrt(n)以内的所有质数(大概是31622),埃氏筛可以承受,我写了线性筛。
然后对于一段区间[m, n],我们枚举这些质数的倍数,然后除去区间内的这些倍数,剩下的就是质数了。
有一点就是,枚举质数时当然不能一倍两倍的枚举。
对于一个质数p,我们可以直接求出一个枚举的最大起点,这个起点满足k * p <= m(k是整数,且k最大)。
由Ruler Function,可以得出k = m / p * p。
还有就是要注意1的特判...
然后就完啦。
#include <cstdio>
#include <cmath>
using namespace std;
const int maxn = 100005, maxsqrtn = 31625;
int prime[maxsqrtn], cnt;
bool isnotprime[maxsqrtn];
inline int iread() {
int f = 1, x = 0; char ch = getchar();
for(; ch < '0' || ch > '9'; ch = getchar()) f = ch == '-' ? -1 : 1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
return f * x;
}
void getprime() {
isnotprime[1] = 1;
for(int i = 2; i < maxsqrtn; i++) {
if(!isnotprime[i]) prime[++cnt] = i;
for(int j = 1; j <= cnt && i * prime[j] < maxsqrtn; j++) {
isnotprime[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
}
int n, m;
bool vis[maxn];
int main() {
getprime();
int T = iread();
while(T--) {
m = iread(); n = iread(); int len = n - m + 1;
for(int i = 0; i < len; i++) vis[i] = 0;
for(int i = 1; i <= cnt && prime[i] <= n; i++) {
for(int j = m / prime[i] * prime[i]; j <= n; j += prime[i]) {
if(j < m || j == prime[i]) continue;
vis[j - m] = 1;
}
}
if(m == 1) vis[0] = 1;
for(int i = 0; i < len; i++) if(!vis[i]) printf("%d\n", i + m);
printf("\n");
}
return 0;
}