【Luogu】P2602 [ZJOI2010] 数字计数 (数位DP)
P2602 [ZJOI2010] 数字计数 - 洛谷
题目:
思路:
数位DP
本题我们要求 a ~ b 中每个数码出现了多少次
根据数位DP的思想,我们可以将答案分成多个限制来求,本题中的答案是每个数码的个数,一次新求出所有个数,那我们可以考虑每次单独求一个数码
所以定义记忆化搜索 dp[i][j] 代表处理 i 位后且当前所求数码是 j 的数量
那么 dfs 过程中就要前导零的影响,如果我们当前要求 0 那么还需要特判,同时还需要传递 sum,即遇到指定数码的个数
具体实现看代码,主要难点在于知道这里要利用数位DP求出每一位的数量,即拆开一个一个求,同时还需要考虑前导零
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"
#define Sunny 0
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());int dp[13][13];
int l, r, len;
int a[13];
int nowchose;
int dfs(int now, int lim, int zero, int sum)
{if (!now)return sum;if (!lim && !zero && dp[now][sum] != -1)return dp[now][sum];int mx = lim ? a[now] : 9;int res = 0;for (int i = 0; i <= mx; i++){int add = 0;if (nowchose > 0)add = (nowchose == i);elseadd = (!zero && nowchose == i);res += dfs(now - 1, lim && i == mx, zero && !i, sum + add);}if (!lim && !zero)dp[now][sum] = res;return res;
}int getans(int x)
{len = 0;while (x)a[++len] = x % 10, x /= 10;return dfs(len, 1, 1, 0);
}void solve()
{cin >> l >> r;for (int i = 0; i < 10; i++){memset(dp, -1, sizeof dp);nowchose = i;cout << getans(r) - getans(l - 1) << " ";}
}signed main()
{ios::sync_with_stdio(false);cin.tie(nullptr);int t = 1;while (t--){solve();}return Sunny;
}