题目:

给定两个整数 l 和 r ,对于所有满足1 ≤ l ≤ x ≤ r ≤ 10^9 的 x ,把 x 的所有约数全部写下来。对于每个写下来的数,只保留最高位的那个数码。求1~9每个数码出现的次数。


做法:

可以拆分成2个区间处理。
问题变成了:统计区间中每个数的因子的最高位数码的出现次数。
例如:区间。
1的因子:1
2的因子:1、2
3的因子:1、3
4的因子:1、2、4
5的因子:1、5
6的因子:1、2、3、6
1作为因子出现了6次,2、3作为因子出现了2次,4、5、6作为因子出现了1次。
1-9数码出现次数:6、2、2、1、1、1、0、0、0。(7-9没出现过)
我们发现:我们不关注每个数有什么因子,我们只关注每个数作为因子出现多少次。
而且我们知道:区间中作为因子出现的次数为。(这是因为的倍数在区间中有个)
如果我们一个一个枚举所有因子算出出现次数,然后统计数量。复杂度的。无法接受。
所以我们不能一个一个统计因子的贡献,要考虑一段一段统计。
我们用整除分块来实现一段一段统计。
什么是整除分块呢?
回到上面那个例子:

因子 1 2 3 4 5 6
出现次数 6 2 2 1 1 1

我们发现有一些连续的因子出现次数是相同的,也就是属于同一段:
经证明: 区间分成了一段一段相同的区间,这个东西就是整除分块了。(证明不会。。
用整除分块实际上就是为了帮我们将区间分成相同的段,分每一段统计贡献。
然后问题就转变成:区间统计次最高位数码的出现次数。
同样拆分区间,处理。
找找规律,考虑数码
1-9中出现了1次。
10-99中出现了10次。
100-999中出现了100次。
...依次类推。
对于某个,设R是个位数,最高位数码为
则数码出现了次、出现了次(括号里一长串其实就是尾数而已)、出现了次。
举个栗子:这个区间统计数码
1、2、3数码作为最高位出现了
4数码作为最高位出现了
5、6、7、8、9数码作为最高位出现了
到这里我们就有了解题的全部信息了。
结合代码理解一下。


代码:

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define debug(a) cout << #a ": " << a << endl
using namespace std;
typedef long long ll;
const int N = 20;
ll a[N], ten[N], prefix[N];
void init(void){//预处理
    ten[1] = 1;
    for (int i = 2; i <= 10; ++i) ten[i] = ten[i-1]*10;
    for (int i = 1; i <= 10; ++i) prefix[i] = prefix[i-1]+ten[i];
}
void calc(int R, ll k){//[1,R]因子算k次贡献
    if (R == 0) return;
    int len = 0, t = R, lim;//len为R数位,lim为R最高位
    while (t){
        len++, lim = t%10, t /= 10;
    }
    //根据规律算贡献。
    for (int i = 1; i < lim; ++i) a[i] += k*prefix[len];
    a[lim] += k*(prefix[len-1]+(R-lim*ten[len]+1));
    for (int i = lim+1; i <= 9; ++i) a[i] += k*prefix[len-1];
}
void solve(int R, int op){//统计[1,R]所有数的因子的最高位数码
    //我们不关心每个数有哪些因子,只关心这个区间中,每个因子出现的次数
    for (int i = 1, j; i <= R; i = j+1){//整除分块(枚举每个因子)
        j = R/(R/i);//因子[i,j]都出现了n/i次。
        calc(j, 1ll*op*(R/i));//加上[1,j]贡献
        calc(i-1, -1ll*op*(R/i));//减去[1,i-1]贡献
    }
}
int main(void){
    IOS;
    init();
    int l, r;
    while (cin >> l >> r){
        solve(r, 1), solve(l-1, -1);
        for (int i = 1; i <= 9; ++i) cout << a[i] << endl;
    }
    return 0;
}