12.2 NOIP 模拟 准考证号

Description

\([a,b]\) 中有多少个不含 \(37\)\(4\) 的数字。

\(1 \leq a, \space b \leq 2 \times 10^9\)

Solution

很明显的一道数位 \(\mathrm{dp}\) 啊。但是我太菜了,并不会数位 \(\mathrm{dp}\)

再仔细一看:数位 \(\mathrm{dp}\) 的数据范围咋开这么小啊,是不是想让打表水过啊。于是我们有了一个新的思路:打表。

可以采用分块的思想,以 \(10^7\) 为一段,把每一段符合条件的数字个数都计算出来。这样一共有 \(200\) 段。制表代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 2e9, unit = 5000000;
int L, R, x, ans, p[23333];

int solve(int l, int r)
{
    p[0] = 0, ans = 0;
    for(int i = l; i <= r; i++)
    {
        int x = i, len = 0, fl = 1;
        while(x != 0) { p[++len] = x % 10; x /= 10; }
        for(int j = 1; j <= len; j++)
            if(p[j] == 4 || p[j] == 3 && p[j - 1] == 7)
                fl = 0;
        ans += fl;
    }
    return ans;
}

int main()
{
    freopen("dabiao.out", "w", stdout);
    for(int i = 1; i <= 400; i++)
    {
        L = unit * (i - 1) + 1, R = unit * i;
        printf("%d, ", solve(L, R));
    }
    return 0;
}

大概一两分钟就能跑出来。

剩下的跟分块一样。扫描打出来的每一段,如果被 \([a,b]\) 区间包含就直接统计答案。区间多余的两边暴力统计。时间复杂度大概是 \(\mathrm{O(200 + 2 \times 8 \times 10^7)}\),极限数据在本地跑大概 \(1.1s\),可以通过。

Code

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int unit = 1e7;
int biao[233] = {0, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435928, 1, 4435929, 4435928, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435928, 1, 4435929, 4435928, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 4435929, 3936807, 1, 4435929, 4435929, 4435929, 4435929, 4435929};
int a, b, L, R, res, flg = 0, p[2333], lx = 0x3f3f3f3f, rx = 0, ans = 0;

inline int solve(int l, int r)
{
    p[0] = 0, res = 0;
    for(int i = l; i <= r; i++)
    {
        int x = i, len = 0, fl = 1;
        while(x != 0) { p[++len] = x % 10; x /= 10; }
        for(int j = 1; j <= len; j++)
            if(p[j] == 4 || p[j] == 3 && p[j - 1] == 7)
                fl = 0;
        res += fl;
    }
    return res;
}

int main()
{
    scanf("%d%d", &a, &b);
    for(int i = 1; i <= 200; i++)
    {
        L = unit * (i - 1) + 1, R = unit * i;
        if(a <= L && b >= R)
        {
            flg = 1;
            lx = min(lx, L);
            rx = max(rx, R);
            ans += biao[i];
        }
        if(L > b) break;
    }
    if(flg && lx > a) ans += solve(a, lx - 1);
    if(flg && rx < b) ans += solve(rx + 1, b);
    if(!flg) ans = solve(a, b);
    printf("%d", ans);
    return 0;
}
posted @ 2020-12-02 11:42  Nyxia  阅读(82)  评论(0编辑  收藏  举报