3122 数字游戏
描述
Alice和Bob又在玩一个游戏。他们从一个数字X0>=3,开始,期望到很大的数字。游戏是这样的:
Alice先走,然后轮流。在第i个回合中,轮到的玩家找一个小于当前数字的素数,然后选择大于当前数字且是找的素数的倍数的最小数。即选择的素数P < Xi-1,Xi>=Xi-1,Xi是P的倍数,注意如果P是Xi-1的约数,那么数字不会变。
L知道了他们两轮后的状态,现在给你一个X2表示两轮后选择的数字,请你确定最小的起始数字X0。特别提醒,玩家不一定每一步选择是最聪明的,你应该考虑所有可能的情况。
输入
输入一个整数X2,保证X2是合数
输出
输出一个整数X0
样例输入
【输入样例1】
14
【输出样例1】
6
【样例解释】
X0=6,
第一轮:Alice选择素数5,并决定这轮数字X1=10
第一轮:Bob选择素数7,并决定这轮数字X2=14
【输入样例2】
20
【输出样例2】
15
【样例解释】
X0=15,
第一轮:Alice选择素数2,并决定这轮数字X1=16
第一轮:Bob选择素数5,并决定这轮数字X2=20
提示
20%数据 X2<=20
40%数据 X2<=2000
100%数据4<=X2<=1000000
知识储备
- 线性筛素数(板子见代码)
- 预处理每个数的最大质因数:
记住两条性质
- 质数的最大质因数为它本身
- 合数的最大质因数为它最大因数的最大质因数(有点递归的感觉)
而线性筛素数保证了每个合数都是被它的最小质因数所筛去(某数=最小质因数*最大因数),所以可以在线性筛素数的同时处理最大质因数的问题
分析
这道题已知x2求x0,那就倒推回去呗。
已知x2,要去找x1,就得明白x2是如何由x1得到的,然后反推,那么根据题意
“找一个小于当前数字(我们现在就指x1)的素数,然后选择大于当前数字(x1)且是找的素数的倍数的最小数”
发现x2是 第一个大于x1的 素数p的倍数(这句话有点绕,多读几遍)
也就是说在x1~x2中间没有p的倍数
所以咯,x2-p+1<=x1<=x2 (p为小于x1的一个质数)
又因为要寻找x0的最小,x1应该尽量小,那么p就应该尽量大,则令p为x2的最大质因数
然后找到x1的范围(别忘了x1必须是合数,因为它也是某个质数的倍数),和上面一样,去找x0
具体实现见代码
代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1000009
using namespace std;
int x2,pri[N],p[N],tot=0;
bool vst[N];
void init(){
int i,j,k;
for(i=2;i<=N;++i){
if(!vst[i]) pri[++tot]=i,p[i]=i;
for(j=1;j<=tot&&pri[j]*i<=N;++j){
vst[pri[j]*i]=1;
p[pri[j]*i]=p[i];
if(i%pri[j]==0) break;
}
}
}
int main(){
scanf("%d",&x2);
init();//线性筛质数,并求出最大质因数
int i,j,k,ans=N;
for(i=x2-p[x2]+1;i<=x2;++i){//枚举x1
if(!vst[i]) continue;//满足x1不为质数才继续
int x0=i-p[i]+1;
ans=min(ans,x0);
}
printf("%d",ans);
return 0;
}