题目描述
给定两个正整数 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%30% 的数据,保证 a≤b≤10^6;
- 对于 100%100% 的数据,保证 1≤a≤b≤10^12。
大概就是魔改过的数位DP板题。设四维状态分别是剩余的长度(或者说当前的位置(从右到左))len,上一个字符last,之前部分是否紧贴上界的判定标志flag,之前部分是否全0的判定标志zero。每个状态的值为一个pair,first是这个状态下一共有多少符合的数,second是这些数里当前要计算的digit(作为dfs的参数)出现的次数。
特别注意有一个地方需要判前导0,详情见代码注释QAQ。
写完翻了翻题解发现有简洁一万倍的写法https://www.luogu.com.cn/blog/bestFy0731/solution-p2602
#include <bits/stdc++.h>
//#define int long long
#define ll long long
using namespace std;
ll a, b, num[65], len;
pair<ll, ll> dp[65][65][2][2];
ll ans[2][10];
pair<ll, ll> dfs(int len, int last, bool flag, bool zero, int digit) {
if(len == 0) return make_pair(1, 0);//注意second是0,一开始写的是last == digit 但是可能和后面的total_digit += sum1;冲突
if(dp[len][last][flag][zero].first != -1) return dp[len][last][flag][zero];//记忆化
ll sum = 0, total_digit = 0;//计算当前状态dp pair用的变量 sum是当前状态的数的个数 total_digit是当前状态中digit出现的总次数
for(int i = 0; i <= 9; i++) {
ll sum1 = 0, total_digit1 = 0;
pair<ll, ll> tmp;
if(((flag && i <= num[len]) || !flag) ) {//当前是可行的(指不超出上界)
tmp = dfs(len - 1, i, flag && (num[len] == i), zero && (i == 0), digit);//搜索下一个状态的套路写法
sum1 = tmp.first;
total_digit1 = tmp.second;
}
sum += sum1;//这个更新是显然的
total_digit += total_digit1;//这个更新也是显然的
if(i == digit && !(zero && i == 0)) total_digit += sum1;//很重要!不能漏掉条件
//这里是说当前遍历的数字正好是digit 这时候需要total_digit += sum1;因为这种状态有sum1个数,每个数前面加上一个digit,对total_digit的贡献就是sum1(这种状态的数的数量)
//注意这里要加上判定条件,即排除前导0的情况
//可能会问为什么前面两条更新语句不需要这个条件,因为前两条当前为0且之前全为0但后继状态的贡献与此无关
//但是下一个状态每个数前面加上一个digit得到的应该是一个实际存在的合法的数,且与当前状态有关,因此需要判定
}
pair<ll, ll> ret;
ret.first = sum;
ret.second = total_digit;
return (dp[len][last][flag][zero] = ret);
}
ll solve(ll x, int digit) {
len = 0;
ll xx = x;
while(xx) {
ll now = xx % 10;
xx /= 10;
len++;
num[len] = now;
}
for(int i = 0; i < 65; i++) {
for(int j = 0; j < 65; j++) {
for(int k = 0; k < 2; k++) {
for(int w = 0; w < 2; w++) {
dp[i][j][k][w].first = -1;
dp[i][j][k][w].second = 0;
}
}
}
}
pair<ll, ll> tmp = dfs(len, 11, 1, 1, digit);
return tmp.second;
}
signed main() {
cin >> a >> b;
for(int i = 0; i < 10; i++) {//对于每个数单独算
cout << solve(b, i) - solve(a - 1, i) << " ";
}
return 0;
}