洛谷 P4999 烦人的数学作业
思路:
数位dp题
设 \(f_{i,j}\) 表示第 \(i\) 位首数码为 \(j\) 涵盖的数的数位之和
那么有转移方程
\[f_{i,j} = \sum_{k=0}^{9}(f_{i-1,k} + j \times c_{i-1,k})
\]
其中 \(c_{i,k}\) 代表第 \(i\) 位首位数码为 \(k\) 的数的个数
当然 , 也可以用\(dfs\)写
将大数按位展开 , 就是一个多叉树 , 设每个边的权为当前位的数字1-9/top , 那么设dfs某个节点可以获得该节点下所有数字的位数和 :
那么dfs树根就是所求结果
如何求dfs树根 ? 需要知道树根相连节点的所有子树dfs结果 , 然后累加
如何知道子树的dfs结果 ? 树根需要提供树根到子树的边权值 , 比如712 , 考虑提供7 , 那么后面从700到799的情况都是有一个"7"在前面顶着的
再如82500到82599 , 00到99的情况枚举中都有一个"8 + 2 + 5"在前面顶着
所以要求cur位的值 , 需要知道cur到根节点的和sum , 以及当前需要到达的所有地方(0-top/9)
等到cur为0 时 , 此时sum就是一条树的路径 , 返回sum即可
那么写dfs的时候 , 决定一个非limit的可能值时 , dfs的携带量只有cur和sum , 所以理所应当建立cur和sum的记忆化搜索
当然 , 也可以参照https://www.cnblogs.com/swyblog/articles/18786587然后用算出的数码个数来计算
代码:
dp版本
#include<bits/stdc++.h>
using namespace std;
#define int long long int
inline int read() {
int ans = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')f = -1;
ch = getchar();
}
while (ch <= '9' && ch >= '0') {
ans = ans * 10 + ch - '0';
ch = getchar();
}
return ans * f;
}
const int mod = 1e9+7;
const int N = 20;
int a[N];
int f[N][10],c[N][10];
void init() {
for (int i =0; i<= 9; i++) {
f[1][i] = i;
c[1][i] = 1;
}
for (int i =2; i< N; i++) {
for (int j = 0; j<= 9; j++) {
for (int k = 0; k< 10; k++) {
c[i][j] += c[i-1][k];
c[i][j]= (c[i][j] + mod)%mod;
}
}
}
for (int i =2; i< N; i++) {
for (int j = 0; j<= 9; j++) {
for (int k = 0; k< 10; k++) {
f[i][j] += f[i-1][k] +mod;
f[i][j] %= mod;
f[i][j] +=( j * c[i-1][k]+mod)%mod + mod;
f[i][j]= (f[i][j] + mod) %mod;
}
}
}
}
int dp(int x) {
int cnt =0,ans = 0;
while (x) {
a[++cnt] = x%10;
ans = (ans + a[cnt]) % mod;
x/=10;
}
int pre = 0;
for (int i =cnt; i>0; i--) {
int now = a[i];
for (int j = 0;j< now; j++) {
ans = (ans + f[i][j] + mod)%mod;
ans = (ans +( pre * c[i][j] + mod)%mod + mod)%mod;
}
pre+=now;
}
return ans;
}
void solve() {
int a = read(),b =read();
cout<<(dp(b) % mod- dp(a-1)%mod + mod) % mod<<"\n";
}
signed main() {
int t = read();
init();
while (t--) solve();
return 0;
}
dfs版本

浙公网安备 33010602011771号