【每日一题】9.数码(整除分块)

题目链接:Here

相关算法:整除分块

题意:

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

思路:

一个很显然的思路是,枚举以 \(x\) 开头的数,计算它的倍数在 \([l, r]\) 中出现的次数。

我们设 \(f(n, i)\)\(i\) 的倍数在 \([1,n]\) 中出现的次数,那么有 \(f(n,i) = \lfloor\frac{n}{i}\rfloor\)

故而ii的倍数在 \([l, r]\) 中出现的次数就是 \(f(r, i) - f(l - 1, i)\)

那么,题目中的问题便是对每个 \(x \in [1,9]\)

\[\sum_{i = x·10^0}^{(x + 1)·10^0 - 1}(f(r,i) - f(l - 1),i) + \sum_{i = x·10}^{(x + 1)·10 - 1}(f(r,i) - f(l - 1),i) + \sum_{i = x·10^2}^{(x + 1)·10^2 - 1}(f(r,i) - f(l - 1),i) + ..... \]

如果直接对枚举的区间进行计算,复杂度是 \(\mathcal{O}(n)\) 的,这显然不能接受。

所以我们需要用到一个“整除分块”的技巧,以达到在 \(\mathcal{O}(\sqrt{n})\) 的复杂度下计算 \(\sum_{i = x}^y\lfloor\frac{n}{i}\rfloor\)

AC 代码

using ll = long long;
ll get(int x, int v) {
    ll res = 0;
    for (ll pw = 1; pw <= x / v; pw *= 10) {
        int cur = v * pw, bound = min<ll>(x, cur + pw - 1);
        // 枚举区间[cur, bound]
        for (int i = cur, j; i <= bound; i = j + 1) {
            j = min(x / (x / i), bound);
            res += 1ll * (j - i + 1) * (x / i);
        }
    }
    return res;
}
void solve() {
    int l, r;
    cin >> l >> r;
    for (int i = 1; i <= 9; i++)
        cout << get(r, i) - get(l - 1, i) << "\n";
}
posted @ 2021-04-16 18:58  Koshkaaa  阅读(77)  评论(1编辑  收藏  举报