[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
这种裸的数位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; }