数位 dp。
设 (dp_{q,i})((iin{0,1,2,3,4,5,6,7,8,9}))为 (1sim q) 中 (i) 出现的次数,(1sim q) 的数字和显然就是 (dp_{q,0} imes 0+dp_{q,1} imes 1+cdots+dp_{q,i} imes icdots+dp_{q,9} imes 9)。
所以我们只需要求出 (1sim q) 中 (i) 出现的次数就能解决这个问题了。
这个问题看起来很好解决,但是注意前导零会影响结果,所以不能有前导零。
这该怎么办呢?
有前导零的式子很容易推出。有 (q) 位数字,(i) 数码的出现次数对于 (xin{smid sin mathbb N,10^qle sle10^{q+1}}) ,(f(q,i)) 的数量都是相等的(设 (f(q,i)) 为 (q) 位数 (i) 数码的出现次数)。
具体求法罢,是:
(egin{cases}f(q,i)=0&q=0\f(q,i)=10f(q-1)+10^{q-1}&q>0end{cases})
我们考虑减去多余的 (0)。
我们先设数字为 (overline{A_1A_2A_3dots A_n})
我们首先考虑求 (overline{A_100dots 0}),将 (overline{A_100dots 0}) 分割为区间 ([0000,1000),[1000,2000),dots,[overline{(A_1-1)00dots 0},overline{A_100dots 0})),所以答案就为 (10^{n-1}A_1),注意 (<A_1) 的每个数还出现了 (10^{n-1}) 次,所以要加上。
首位 (A_1) 出现了 (overline{A_2A_3dots A_n}+1) 次,答案还要加上 (overline{A_2A_3dots A_n}+1),
当然还需要处理前导 (0),用排列组合算一下会知道 (i) 位 (q) 个前导零的数量就是 (10^q)((qin{smid sinmathbb N,0le sle i-1})),把它们加起来会发现一共出现了 (10^{i-1}+10^{i-2}+...10)((=sumlimits_{k=0}^{i-1}10^k)) 次,减一下即可。
Code:
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=51,MOD=1e9+7; //注意能 MOD 的地方都要 MOD,不然会 WA 0pts。
ll pow10[N],dp[N],a[N],count[N],tmpcount[N],ans;
// pow10 : 字面意思,10^n
// dp : 不考虑前导零的状况
// count : 统计 0~9 出现次数
// tmpcount : 暂时保存 count,用来减
// ans : 累加答案
void init() //预处理 pow10 和 dp。
{
pow10[0]=1;
for (int i=1;i<30;i++) dp[i]=(dp[i-1]*10%MOD+pow10[i-1])%MOD,pow10[i]=10*pow10[i-1]%MOD;
}
void solve(ll x)
{
int len=0;
while (x){a[++len]=x%10;x/=10;} //数位分离
for (int i=len;i>=1;i--) //从高到低遍历
{
for (int j=0;j<10;j++) count[j]+=dp[i-1]*a[i],count[j]%=MOD; //分割区间
for (int j=0;j<a[i];j++) count[j]+=pow10[i-1],count[j]%=MOD; //加上 10^(n-1)
ll lastnum=0;
for (int j=i-1;j>=1;j--) lastnum=lastnum*10+a[j],lastnum%=MOD; //求出 A2A3A4...An
count[a[i]]+=lastnum+1,count[a[i]]%=MOD;
count[0]-=pow10[i-1],count[0]=(count[0]+MOD)%MOD; //减去前导零
}
}
int main()
{
init();
ll l,r,T;
cin>>T;
for (int q=0;q<T;q++)
{
ans=0; cin>>l>>r;
solve(r); //前缀和思想相减 r 和 l-1。
for (int i=0;i<10;i++) (tmpcount[i]=count[i]),count[i]=0; //复制 count,记得清零
solve(l-1);
for (int i=0;i<10;i++) ans=(ans+i*(tmpcount[i]-count[i]+MOD)%MOD)%MOD,count[i]=0; //累加答案,记得清零 count。
cout<<ans<<'
';
}
return 0;
}
Refence 求数字 (i) 出现的次数。