[BZOJ1833][ZJOI2010]数字计数

Description

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

Input

输入文件中仅包含一行两个整数a、b,含义如上所述。

Output

输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。

Sample Input

1 99

Sample Output

9 20 20 20 20 20 20 20 20 20

HINT

30%的数据中,a<=b<=10^6;
100%的数据中,a<=b<=10^12。

Source

Day1


这种裸的数位Dp只有我这蒟蒻不会做吧...

设f[i][j][k]为长度为i, 开头为j, k这个数字出现的次数。

然后就不会了。


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

typedef long long ll;
struct date
{
    ll num[10];
    friend date operator + (date a, date b)
    {
        date t;
        for (int i = 0 ; i <= 9 ; i ++) t.num[i] = b.num[i] + a.num[i];
        return t;
    }
}f[25][10];
ll a, b, t[25];
date cal(ll x)
{
    date ans;
    for (int i = 0 ; i <= 9 ; i ++) ans.num[i] = 0;
    if(!x){ans.num[0]=1;return ans;}
    int len = 15;
    while(t[len] > x) len--;
    for (int i = 1 ; i < len ; i ++)
        for (int j = 1 ; j <= 9 ; j ++)
            ans = ans + f[i][j];
    ans.num[0]++;
    int lim = x / t[len];
    for (int i = 1 ; i < lim ; i ++) ans = ans + f[len][i];
    x %= t[len];
    ans.num[lim] += x + 1;
    for (int i = len - 1 ; i ; i --)
    {
        lim = x / t[i];
        for (int j = 0 ; j < lim ; j ++) ans = ans + f[i][j];
        x %= t[i];
        ans.num[lim] += x + 1;
    }
    return ans;
}


int main()
{
    t[1] = 1;for (int i = 2 ; i <= 15 ; i ++) t[i] = t[i-1] * 10;
    for (int i = 0 ; i <= 9 ; i ++) f[1][i].num[i] = 1;
    for (int i = 2 ; i <= 12 ; i ++)
    {
        for (int x = 0 ; x <= 9 ; x ++)
        {
            for (int y = 0 ; y <= 9 ; y ++)
            {
                f[i][y] = f[i][y] + f[i-1][x];
                f[i][y].num[y] += t[i-1];
            }
        }
    }
    scanf("%lld%lld", &a, &b);
    date t1 = cal(a-1), t2 = cal(b);
    for (int i = 0 ; i <= 8 ; i ++) printf("%lld ",t2.num[i] - t1.num[i]);
    printf("%lld", t2.num[9] - t1.num[9]);
    return 0;
}
原文地址:https://www.cnblogs.com/BriMon/p/9370700.html