思路
记忆化搜索。很明显这题的输入一定是字符串。那么我们还需要写一个字符串减法,来计算左端点减一的值。
题目要求计算区间 l ∼ r l \sim r l∼r 内有趣的数字的数量。那么 1 ∼ r 1 \sim r 1∼r 的有趣数字的数量减去 1 ∼ l − 1 1 \sim l-1 1∼l−1 的数量就是区间内有趣数字的数量。
那我们可以用记忆化搜索的方式就行计算。记忆化搜索只需要三个参数。当前构造到的位置 n o w now now,上一个数字 l a s t last last,以及当前构造的数字是否顶格 l l l。
使用 l a s t last last 参数来判断当前数字是否按非递减顺序排列。使用 l l l 参数来判断当前数位上所能取最大值。
因为记忆化搜索是在每一位上构造符合要求的数字,所以当数字构造完毕后一定也符合题目要求,直接返回 1 1 1 即可。
不要忘记取模。
代码
#include<cstring>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#include<map>
#define ll long long
#define lhs printf("\n");
using namespace std;
const int N=3e5+10;
const int M=1e9+7;
const int inf=0x3f3f3f3f;
string l,r,a;
int a1[N],a2[N],ans[N];
ll dp[114][20][5];
string jian(string s1,string s2)
{string h;for(int i=0;i<s1.size();i++){a1[s1.size()-1-i]=s1[i]-'0';}for(int i=0;i<s2.size();i++){a2[s2.size()-1-i]=s2[i]-'0';}for(int i=0;i<max(s1.size(),s2.size());i++){ans[i]=a1[i]-a2[i];}for(int i=0;i<max(s1.size(),s1.size());i++){if (ans[i]<0){ans[i]+=10;ans[i+1]--;}}int k=N-1;while((!ans[k]) and k>0){k--; }for(int i=k;i>=0;i--){char x=char(ans[i]+48);h+=x;}return h;
}
ll dfs(int now,int last,int l)
{if(now==a.size()){return 1;}if(dp[now][last][l]!=-1){return dp[now][last][l];}int k=int(a[now])-48;int b=l ? k : 9;ll ans=0;for(int i=0;i<=b;i++){if(i>=last){ans+=dfs(now+1,i,l and i==b);ans%=M;}} return dp[now][last][l]=ans;
}
ll solve(string x)
{memset(dp,-1,sizeof dp);a.clear();a+=x;return dfs(0,0,1);
}
int main()
{cin>>l>>r;cout<<(solve(r)-solve(jian(l,"1"))+M)%M;//减法取模要+MODreturn 0;
}