某年暑假的时候,QIU很喜欢玩跑跑卡丁车,因为跑跑卡丁车能够在线联网,所以他就能跟自己的朋友一齐很开心的在同一跑道上比赛。
跑跑卡丁车的地图很多样,但是模式都差不多,在道具模式中,我们可以得到一些很有利的道具,帮助我们尽快的到达终点,或者妨碍对手前进。
为了简化模型,我们假设地图上只有一种道具(金币),如果你检了的话,你就可以增加自己的财富(¥¥),而地图中通常都有很多个点,很多条路,
每个点上存在不同的金币数量(不过捡了就没了),当你钱包的金币数达到一定的数量时,你就可以利用这些金币买到你喜欢的东西了_。
当然,你肯定希望每一场得到的金币数目越多越好咯。
XUN也很喜欢这个游戏,由于对数学的爱好,XUN发现了另一个很好玩的玩法,对于每场比赛,她并不仅仅想知道自己赚了多少金币,她还想知道今天跟昨
天钱包的金币数量范围内,到底哪些神奇的数相差最小(最近),哪些神奇的数相差最大(最远)。
我们定义神奇的数为一些数,只能整除本身还有1。
由于QIU比较笨,不会回答XUN的问题,聪明的ACMER你能帮一下QIU吗?
输入格式
第一行,给出一个整数T,表示case数量
下面T行,每一行给出两个正整数L,R,L < R,R – L <=1000000,1 <= L < R <= 2147483647
输出格式
对于每一对L,R,输出最近的两个神奇的数,还有最远的两个神奇的数,如果没有相邻的两个神奇的数,输出”None.”
如果存在多对最近或最远的数,输出最小那对。
输入样例
2
21 987
78 80
输出样例
Closest: 29 31
Longest: 887 907
None.
题意:找出【L,R】区间内相邻最近和最远的质数,没有就输出none |
思路:
和AcWing上一道“质数距离”的题一样,下面说一下思路:
1.任何一个合数都可以写成 n = p*q 的形式, 其中p<=根号n, q>=根号n。这个显然啦,一个数的因数分布在根号n两侧。
2.观察这个题,L,R各自的大小都能到2e9左右,就连最简单的线性筛都搞不到这么大,更别说其他什么暴力算法。
3.但是发现破题口在于区间长度:R-L+1 <= 1e6。这个地方怎么入手呢?诶,看看步骤1,这个区间内的所有合数都满足上述等式,那么,我们就可以把根号1e9以内的素数全部筛出来(这个普通线性筛很快就搞出来),然后用里面的每个素数去筛掉【L.R】里面的合数,那么剩下来的就是素数啦。
AC代码:
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(long long i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 1e5+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0', ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };
ll prime[maxn], tot = 0;
bool vis[maxn*10];
ll ans[maxn];
void init()
{
prime[tot++] = 2; vis[4] = 1;
for(int i=3; i<=maxn; i += 2)
{
if(!vis[i]) prime[tot++] = i;
for(int j=0; j<tot&&prime[j]*i<=maxn; j++)
{
vis[i*prime[j]] = 1;
if(i%prime[j]==0) break;
}
}
}
int main()
{
init();
ll L, R;
int kase;
cin>>kase;
while(~scanf("%lld%lld",&L,&R))
{
mem(vis,0);
rep(i,0,tot-1) //外层遍历素数
{
if(prime[i]>R) break; //如果当前这个超过区间上限就break掉
ll cur = (L+prime[i]-1)/prime[i]; //这一步是找到[L,R]里面当前质数的最小倍数/该质数后的因子,其实就是L/prime[j]的向上取整
for(ll j=cur; j*prime[i]<=R; j++) if(j!=1) //然后枚举倍数,从cur倍, 到cur+1倍,到cur+2倍,等等。直到超过区间边界
vis[j*prime[i] -L] = 1; //那么这个数j*prime[j]就是个合数啦,因为数很大,就-L这个偏移量,否则下标太大。
}
ll p = 0;
ll dis = 1e12 , a = 0, b = 0 ;
rep(i,L,R) if(!vis[i-L]&&i!=1) ans[++p] = i;
if(p < 2) cout<<"None."<<'\n';
else
{
rep(i,1,p-1) if(dis > abs(ans[i] - ans[i+1])) dis = abs(ans[i] - ans[i+1]) ,a = ans[i], b = ans[i+1];
dis = 0; ll c = 0, d = 0;
rep(i,1,p-1) if(dis < abs(ans[i] - ans[i+1])) dis = abs(ans[i] - ans[i+1]) ,c = ans[i], d = ans[i+1];
printf("Closest: %lld %lld\n",a,b);
printf("Longest: %lld %lld\n",c,d);
}
}
return 0;
}