[P2602] [ZJOI2010]数字计数(数位dp)

原题

题目描述

给定两个正整数 (a)(b),求在 ([a,b]) 中的所有整数中,每个数码(digit)各出现了多少次。

输入格式

仅包含一行两个整数 (a,b),含义如上所述。

输出格式

包含一行十个整数,分别表示 (0∼9)([a,b])中出现了多少次。

输入输出样例

输入 #1

1 99

输出 #1

9 20 20 20 20 20 20 20 20 20

说明/提示

数据规模与约定

  • 对于 (30\%) 的数据,保证 (a≤b≤10^6)
  • 对于 (100\%) 的数据,保证 (1≤a≤b≤10^{12})

思路

数位dp套模板,注意前导零。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <list>
#include <map>
#include <iostream>
#include <iomanip>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <vector>
#define LL long long
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f
#define PI 3.1415926535898
#define F first
#define S second
#define endl '
'
#define lson rt << 1
#define rson rt << 1 | 1
#define lowbit(x) (x & (-x))
#define f(x, y, z) for (int x = (y), __ = (z); x < __; ++x)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define _per(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;
LL dp[20][20];
LL l, r;
int a[20], tar;
inline LL dfs(int pos, int cnt, bool lead, bool limit)
{
    if (pos == -1)
        return cnt;
    if (!limit && !lead && dp[pos][cnt] != -1)
        return dp[pos][cnt];
    int up = limit ? a[pos] : 9;
    LL ans = 0;
    for (int i = 0; i <= up; i++)
    {
        ans += dfs(pos - 1, cnt + ((!lead || i) && (i == tar)), lead && (i == 0), limit && (i == a[pos]));
    }
    if (!limit && !lead)
        dp[pos][cnt] = ans;
    return ans;
}

inline LL solve(LL x, int num)
{
    memset(dp, -1, sizeof(dp));
    tar = num;
    int pos = 0;
    while (x)
    {
        a[pos++] = x % 10;
        x /= 10;
    }
    a[pos] = 0;
    return dfs(pos - 1, 0, true, true);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> l >> r;
    _rep(i, 0, 9)
    {
        cout << solve(r, i) - solve(l - 1, i) << " ";
    }
}
原文地址:https://www.cnblogs.com/hfcdyp/p/14136847.html