【Eratosthenes筛法】报数

        吃狗粮已经吃撑了,所以今晚开道题消化消化。随机跳题。上……等等,这不是去年NOIP题吗?当时汤圆刚入门C++很菜,这题都不会做,正好重新试一下,上题目:

题目描述

        报数游戏是一个广为流传的休闲小游戏。参加游戏的每个人要按一定顺序轮流报数,但如果下一个报的数是7的倍数,或十进制表示中含有数字7,就必须跳过这个数,否则就输掉了游戏。

        在一个风和日丽的下午,刚刚结束 SPC20nn 比赛的小 r 和小 z 闲得无聊玩起了这个报数游戏。但在只有两个人玩的情况下计算起来还是比较容易的,因此他们玩了很久也没分出胜负。此时小 z 灵光一闪,决定把这个游戏加强:任何一个十进制中含有数字7的数,它的所有倍数都不能报出来! 

        形式化地,设p(x)表示x的十进制表示中是否含有数字7,若含有则p(x)=1,否则p(x)=0。则一个正整数x不能被报出,当且仅当存在正整数yz ,使得x=yzp(y)=1。例如,如果小 r 报出了6,由于7不能报,所以小 z 下一个需要报8;如果小 r 报出了33,则由于34=17\times 235=7\times5都不能报,小 z 下一个需要报出36;如果小 r 报出了69,由于70\sim79的数都含有7,小 z 下一个需要报出80才行。

        现在小 r 的上一个数报出了x,小 z 想快速算出他下一个数要报多少,不过他很快就发现这个游戏可比原版的游戏难算多了,于是他需要你的帮助。当然,如果小 r 报出的x本身是不能报出的,你也要快速反应过来小 r 输了才行。

        由于小 r 和小 z 玩了很长时间游戏,你也需要回答小 z 的很多个问题。

输入格式

        第一行,一个正整数T表示小 z 询问的数量。接下来T行,每行一个正整数x,表示这一次小 r 报出的数。

输出格式

        输出共T行,每行一个整数,如果小 r 这一次报出的数是不能报出的,输出-1,否则输出小 z 下一次报出的数是多少。

样例输入

4
6
33
69
300

样例输出

8
36
80
-1

数据规模与约定

        对于100%的数据,1 \le T \leq 2 \times {10}^51 \le x \leq {10}^7


解题

        首先要明白,为什么跳过的数字是7,而不是别的数字?

        显然是我想多了,只是巧合而已!

        现在正经解题。

        这种题一般不会让你通过暴力的方法得满分,所以还是要仔细审题,抓住要点。审题时,注意到这样一句话:

 任何一个十进制中含有数字7的数,它的所有倍数都不能报出来! 

        发现这一叙述和Eratosthenes筛法的一条叙述有着相似之处:

        (关于Eratosthenes筛法,汤圆以后会详细讲解。)

任何一个质数,(除了它自身外)它的所有倍数都不是质数。

        那这题就有基本思路了:先用筛法扫一遍({10}^7的数据规模是可以接受的),同时用数组存储信息。此后的每次询问,都可以在时间复杂度O(1)下给出回答。

        需要注意的一些细节:

  1. 不要正好筛到{10}^7!一定多筛一点!!否则,问你{10}^7的下一个数是多少,你不就无言以对?
  2. 判断一个数是否含有数字7,也没有什么捷径,直接按位判断就好。
  3. 如果一个数不能报出,询问它的下一个数是没有意义的。

        一切准备就绪,现在来写代码:(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;
	}


        感觉其实还是挺简单的。好了,做完题还要吃狗粮。感谢大家的支持,我是汤圆,我们下期再见!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值