吃狗粮已经吃撑了,所以今晚开道题消化消化。随机跳题。上……等等,这不是去年NOIP题吗?当时汤圆刚入门C++很菜,这题都不会做,正好重新试一下,上题目:
题目描述
报数游戏是一个广为流传的休闲小游戏。参加游戏的每个人要按一定顺序轮流报数,但如果下一个报的数是的倍数,或十进制表示中含有数字
,就必须跳过这个数,否则就输掉了游戏。
在一个风和日丽的下午,刚刚结束 SPC20nn 比赛的小 r 和小 z 闲得无聊玩起了这个报数游戏。但在只有两个人玩的情况下计算起来还是比较容易的,因此他们玩了很久也没分出胜负。此时小 z 灵光一闪,决定把这个游戏加强:任何一个十进制中含有数字的数,它的所有倍数都不能报出来!
形式化地,设表示
的十进制表示中是否含有数字
,若含有则
,否则
。则一个正整数
不能被报出,当且仅当存在正整数
和
,使得
且
。例如,如果小 r 报出了
,由于
不能报,所以小 z 下一个需要报
;如果小 r 报出了
,则由于
,
都不能报,小 z 下一个需要报出
;如果小 r 报出了
,由于
的数都含有
,小 z 下一个需要报出
才行。
现在小 r 的上一个数报出了,小 z 想快速算出他下一个数要报多少,不过他很快就发现这个游戏可比原版的游戏难算多了,于是他需要你的帮助。当然,如果小 r 报出的
本身是不能报出的,你也要快速反应过来小 r 输了才行。
由于小 r 和小 z 玩了很长时间游戏,你也需要回答小 z 的很多个问题。
输入格式
第一行,一个正整数表示小 z 询问的数量。接下来
行,每行一个正整数
,表示这一次小 r 报出的数。
输出格式
输出共行,每行一个整数,如果小 r 这一次报出的数是不能报出的,输出
,否则输出小 z 下一次报出的数是多少。
样例输入
4
6
33
69
300
样例输出
8
36
80
-1
数据规模与约定
对于的数据,
,
。
解题
首先要明白,为什么跳过的数字是,而不是别的数字?
显然是我想多了,只是巧合而已!
现在正经解题。
这种题一般不会让你通过暴力的方法得满分,所以还是要仔细审题,抓住要点。审题时,注意到这样一句话:
任何一个十进制中含有数字
的数,它的所有倍数都不能报出来!
发现这一叙述和Eratosthenes筛法的一条叙述有着相似之处:
(关于Eratosthenes筛法,汤圆以后会详细讲解。)
任何一个质数,(除了它自身外)它的所有倍数都不是质数。
那这题就有基本思路了:先用筛法扫一遍(的数据规模是可以接受的),同时用数组存储信息。此后的每次询问,都可以在时间复杂度
下给出回答。
需要注意的一些细节:
- 不要正好筛到
!一定多筛一点!!否则,问你
的下一个数是多少,你不就无言以对?
- 判断一个数是否含有数字
,也没有什么捷径,直接按位判断就好。
- 如果一个数不能报出,询问它的下一个数是没有意义的。
一切准备就绪,现在来写代码:(C++20 O2)
#include <cstdio>
#define N 10001000
int f[N + 1], next[N + 1], last, t, k;
int check(int i) {
while (i) {
if (i % 10 == 7)
return 1;
i /= 10;
}
return 0;
}
int main() {
for (int i = 1; i <= N; i++) {
if (f[i] == 1) // 如果当前的数已知不能报出,则下一个数
continue;
if (i % 7 == 0)
f[i] = 1;
else if (check(i))
for (int j = i; j <= N; j += i)
f[j] = 1;
else {
next[last] = i; // last是上一个可报出的数
last = i; // i也是可报出的,更新last的值
}
}
scanf("%d", &t);
while (t--) {
scanf("%d", &k);
if (f[k])
printf("-1\n");
else
printf("%d\n", next[k]);
}
return 0;
}
感觉其实还是挺简单的。好了,做完题还要吃狗粮。感谢大家的支持,我是汤圆,我们下期再见!