【NOIP2021】报数题解

本蒟蒻的第一篇题解

此题作为今年NOIP唯一一道略简单 普及— 难度的题,做法与某种强大的素数筛法(埃氏筛法)雷同,具体做法如下:
 
我们先来看题目:
    
如果下一个报的数是 7 的倍数,或十进制表示中含有数字 7,就必须跳过这个数。任何一个十进制中含有数字 7 的数,它的所有倍数都不能报出来。
    
我们可以发现,这和埃氏筛法的思想很像,其实就是模拟,我们可以用一个vis数组来记录一个数是否应该被跳过。

看代码:

void init () {
    for (int i = 1; i <= 10000000; i++) {
        if (!vis[i] && (seven(i) || i == 7)) {
            for (int j = 1; i * j <= inf; j++) {
                vis[i * j] = true;
            }
        }
    }
}

其中的seven函数是用来判断这个数中是否包含7的

bool seven (int x) {
    while (x) {
        if (x % 10 == 7) return true;
        x /= 10;
    }
    return false;
}

这样我们就预处理好了10^7以内的数是否应该被跳过

于是乎,我就交了一发这样的代码:

#include <iostream>
using namespace std;
int t, x;
bool vis[10000010];
const int inf = 10000010;
bool seven (int x) {
    while (x) {
        if (x % 10 == 7) {
            return true;
        }
        x /= 10;
    }
    return false;
}
void init () {
    for (int i = 1; i <= 10000000; i++) {
        if (!vis[i] && (seven(i) || i == 7)) {
            for (int j = 1; i * j <= inf; j++) {
                vis[i * j] = true;
            }
        }
    }
}
int main () {
    init();
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &x);
        if (vis[x]) printf("-1\n");
        else {
            for (int i = x + 1; i; i++) {
                if (!vis[i]) {
                    printf("%d\n", i);
                    break;
                }
            }
        }
    }
    return 0;
}

你以为这样就结束了吗?

非也,非也。

我荣幸地拿到了70分,T了三个点。

经过我详细的计算粗略的猜测,我觉得可能会多次询问同一个数,我就开始改进while循环中的代码。我想到了老师说过的记忆化搜索(用空间换时间玄学大法),就用f数组来记录已经询问过的数。

没想到真的就这样卡过去了。。。

附上AC代码


#include <iostream>
using namespace std;
int t, x;
bool vis[10000010];
int f[10000010];
const int inf = 10000010;
bool seven (int x) {
    while (x) {
        if (x % 10 == 7) return true;
        x /= 10;
    }
    return false;
}
void init () {
    for (int i = 1; i <= 10000000; i++) {
        if (!vis[i] && (seven(i) || i == 7)) {
            for (int j = 1; i * j <= inf; j++) {
                vis[i * j] = true;
            }
        }
    }
}
int main () {
    init();
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &x);
        if (f[x] != 0) {
            printf("%d\n", f[x]);
            continue;
        }
        if (vis[x]) printf("-1\n");
        else {
            for (int i = x + 1; i; i++) {
                if (!vis[i]) {
                    printf("%d\n", i);
                    f[x] = i;
                    break;
                }
            }
        }
    }
    return 0;
}
上一篇:NOIP2021 简要复盘/题解


下一篇:NOIP2021 比赛记录