这道题目实际上我们首先看看题目描述
摘樱桃(cherry) 拉娜生活在一个美丽的小村庄。在大街的一旁有一排樱桃树,编号从1到N. 在反复研究后,拉娜发现树的编号神奇的决定了这棵树的樱桃数。 对每一棵树,考虑树的编号中连续的几个数段,对每一数段,该数字乘上这个数段的长度的平方再全部相加,就得到这棵树能够结的樱桃数。 比如,树的编号是77744007,数段分别为777,44,00和7。樱桃数就是7*3^2+4*2^2+0*2^2+7*1^2=86个。 摘樱桃的时候到了,村民们同意把编号为A到B(包括A,B)的所有樱桃都摘下来。写一个程序计算,能摘到多少樱桃。
输入样例一:
1 9
输入样例二:
100 111
输入样例三:
7774407 7774407
输出样例一:
45
输出样例二:
68
输出样例三
86
首先我们可以发现令 f(i) 表示从1-i中的可以摘的樱桃,那么答案应该就是 ans=f(B)−f(A−1) 那么可以发现这是一个数位DP那么令 f(i,j,d) 表示当第i位为j的数字的所有的樱桃的数量和第三位表示的是当前构成 f(i,j,d) 的编码和是否严格小于A或者B的前i位所构成的数字(因为要特殊处理边界)(i从小到大对应的是A的从高位到个位)
然后分析一下很容易发现
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 16;
LL dp[MAXN+2][10][2], g[MAXN+2][10][2], B[MAXN+10], n;
LL f(int i, int j, int d){
if(!i) return 0;
if(dp[i][j][d]) return dp[i][j][d];
if(d){
int nowp=i;
do{nowp--;}while(B[nowp+1] == B[nowp]);
dp[i][j][d] = f(nowp, B[nowp], d) + 1LL * (i - nowp) * (i - nowp) * j * g[nowp][B[nowp]][1];
}else{
bool flag = B[i] <= j;
for(int k=i-1; k>=0; k--){
if(B[k+1] == j) flag=flag;
else flag=j>B[k+1];
int len = i - k;
for(int z=0;z<10;z++)
if(z != j)
dp[i][j][d] += f(k, z, 0) + 1LL * len * len * j * g[k][z][0];
if(flag) continue;
if(B[k] != j || !k)
dp[i][j][d] += f(k, B[k], 1) + 1LL * len * len * j * g[k][B[k]][1];
}
}
return dp[i][j][d];
}
LL solve(LL u){
if(!u) return 0;
memset(dp, 0, sizeof dp);
memset(g, 0, sizeof g);
g[0][0][1] = 1LL;
char tmp[MAXN+5];
memset(tmp, 0, sizeof tmp);
int pos = 0;
while(u){
tmp[pos++] = u % 10 + '0';
u = u / 10;
}
n = strlen(tmp);
for(int i=0;i<n;i++)
B[i+1] = tmp[n-i-1]-'0';
for(int i=1;i<=n;i++){
for(int j=0;j<10;j++){
for(int k=0;k<10;k++)
g[i][j][0] += g[i-1][k][0];
if(j<B[i]) g[i][j][0] += g[i-1][B[i-1]][1];
}
g[i][B[i]][1] = 1;
}
LL ret = f(n, B[n], 1);
for(int z=0;z<10;z++)
ret += f(n, z, 0);
return ret;
}
int main(){
LL a, b;
while(cin>>a>>b){
cout<<solve(b) - solve(a-1)<<endl;
}
return 0;
}