解题思路
很明显这是一道数位dp的题目
求出一个区间内每一个数字的上每一位的数字和
首先暴力肯定是不可能的,数据上限太大了
那么我们可以借助数位dp的想法 , 既然我要求出[ l ,r ]内的数字总和,我可以求出
[
1
,
r
]
[1 , r ]
[1,r]的数字和减去
[
1
,
l
−
1
]
[1 , l-1 ]
[1,l−1]的数字和即可
那么问题就转化为了求出 [ 1 , n ] [1 , n ] [1,n]内的数字和是多少
第一步肯定是把数字拆开,并初始化数组
ll solve(ll o)
{
v.clear();
do{
v.push_back(o % 10);
o /= 10;
}while (o);
//初始化数组
memset(dp,-1,sizeof dp);
return dfs(v.size() , 0 , 1);
}
接下来就是核心部分
- pos : 目前遍历到了第几位
- sum:到当前这一位的时候,前面几位的数字和是多少
- limit:是否有限制,最大数是固定的n,假设n=12345,那么此时检索到第三位的时候,前面两位分别是1和0,此时第三位取0~9任意一个数字都是成立的,如果前两位是1和2,那么这一位范围就是0~3
ll dfs(int pos, int sum , bool limit)
{
//遍历结束了,返回sum值
if(!pos) return sum;
//记忆化搜索
if(~dp[pos][sum][limit]) return dp[pos][sum][limit];
//根据limit来设置上限
int maxn = (limit ? v[pos-1] : 9);
ll res = 0;
for(int i=0;i<=maxn;i++){
//前进一位,sum记录一下和
//这里说明一下a[pos-1] == i一定要结合i<=maxn来看
res = (res + dfs(pos-1 , sum + i , limit && (i == v[pos-1])));
}
//记忆化搜索
return dp[pos][sum][limit] = res;
}
细节讲解
一定不要忘了取模,仔细读题
并且涉及到减法运算,很可能会出现的问题是
a > b , 但是 (a % mod) < b
这就会导致a-b变成负数
所以解决方法就是(a + b + mod) % mod
总代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e9 + 7;
int n;
vector<int> v;
ll dp[200][400][2];
ll dfs(int pos, int sum , bool limit)
{
//遍历结束了,返回sum值
if(!pos) return sum;
//记忆化搜索
if(~dp[pos][sum][limit]) return dp[pos][sum][limit];
//根据limit来设置上限
int maxn = (limit ? v[pos-1] : 9);
ll res = 0;
for(int i=0;i<=maxn;i++){
//前进一位,sum记录一下和
//这里说明一下a[pos-1] == i一定要结合i<=maxn来看
res = (res + dfs(pos-1 , sum + i , limit && (i == v[pos-1])));
}
//记忆化搜索
return dp[pos][sum][limit] = res;
}
ll solve(ll o)
{
v.clear();
do{
v.push_back(o % 10);
o /= 10;
}while (o);
memset(dp,-1,sizeof dp);
return dfs(v.size() , 0 , 1);
}
int main()
{
cin >> n;
long long a,b;
while (n--){
cin >> a >> b;
cout << (solve(b) - solve(a-1) + N) % N << endl;
}
return 0;
}
思路拓展
我又想到另一个思路
完全可以算出来
[
1
,
n
]
[ 1 , n ]
[1,n]中的每个数字出现的个数,然后累加起来就行了
这就完全是数位dp的基础例题。