题解:

菜鸟讲解版

很明显的数论分块。等等,什么是数论分块???懂数论分块的直接跳过以下一部分。
以下所有分式除法均为整除。
----------分割线
数论分块,顾名思义,将一段连续的区间分成一块一块相同的区间。
简单点,将[1,n]这段区间来分块,分成,,。k个块。
属于同一个块之间的元素满足
好了,我们已经知道数论分块分成什么块了。接下来是怎么分?
显然,如果则有。所以块中元素依然连续,相当于在原先的数轴[1,n]剪成k个块。
那么想要怎么分就等价于怎么确定一个块的两个端点了。显然第一个块的左端点是,因为我们只考虑区间,那么如果确定了左端点(设为)后,右端点(设为)怎么确定呢?
答案是,

证明:,即
,所以,即所以
所以,这就证明了,属于同个块。

,即,即不属于同个分块。
即说明即为所在块的右端点。
证毕。
由于块与块之间的连续性,则即为下一个块的左端点。
好了,怎么分已经知道了,那么一共会分成多少块呢?
我们回到上面,属于同一个块的依据是整除于块中元素互相相当,也就是有多少个不同的数就有多少个块。
答案:为量级,常数在1-2之间。
证明:当时,即使得到的每个数都不同,结果最多也只有
,最多也为
即用到数论分块时,时间复杂度为
----------分割线
懂得数论分块就简单的多了,问题求解的是区间,可以转化成
所以问题装化为
我们先来看一个问题,在区间中有多少个数的约数含有,答案为,害,这个不用证明。
所以我们可以通过枚举约数来计算对数码的贡献,但是一个个枚举,复杂度高达O(R)了,显然承受不起(牛客评测机太牛逼,这样搞过了不关我事),所以,就要用数论分块来搞一搞了,我们可以不用一个个约数枚举,而是一段段约数枚举了,因为我们发现。枚举了一段约数区间后,假设为且区间每个数会出现次,其对数码的贡献是多少呢?为此我们分出一个子问题:在中的数对数码的贡献,同样,把这个区间分成
问题渐渐变得简单了,现在就是要解决这个子问题了,我们发现对每个数码的贡献都是1,对每个数的贡献是10,...聪明的你已经发现,只需要知道的最高位,为最高位是第几位,就可以直接算贡献了。

大佬讲解版题解:

一眼过去就是数论分块呀,随便搞搞就可以了。

代码:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
ll a[10];
int b[10];
void get(int n,int s)//计算1-9作为最高位在[1,n]区间的出现的个数*s
{
    int t=0;//t记录n是最高位是第几位数
    int m=n,g=0;//g记录最高位为多少
    while(m)
    {
        t++;
        g=m%10,m/=10;
    }
    for(int i=1;i<=9;i++)a[i]+=1ll*s*b[t-1];//在用到最高位之前,对每个数码的贡献是一样的
    for(int i=1;i<g;i++)a[i]+=1ll*s*(b[t]-b[t-1]);//
    a[g]+=1ll*s*(n-g*(b[t]-b[t-1])+1);//
}
void solve(int n,int op)//数论分块
{
    int j;
    for(int i=1;i<=n;i=j+1)
    {
        j=n/(n/i);
        get(j,op*n/i);//问题中的子问题
        get(i-1,-op*n/i);
    }
}
int main()
{
    int l,r;
    cin>>l>>r;
    int ans=1;
    for(int i=1;i<10;i++)//pow(10,i)之内对各个数码的贡献
    {
        b[i]=b[i-1]+ans;
        ans*=10;
    }
    solve(r,1);
    solve(l-1,-1);
    for(int i=1;i<=9;i++) cout<<a[i]<<endl;
}