牛客 数码
题意
对于所有满足属于区间 [ l , r ] [l,r] [l,r] 的 n u m num num,把 n u m num num 所有因子写下来,对于写下来的数,只保留最高位数,求 1 − 9 1-9 1−9 每个数出现的次数。
题解
- 为了简化问题,可以求从 [ 1 , l − 1 ] [1,l-1] [1,l−1] 和 [ 1 , r ] [1,r] [1,r] 的情况,然后做差得到 [ l , r ] [l,r] [l,r] 的答案,这样可以不用管左边界,接下来就是要求 [ 1 , n ] [1,n] [1,n] 的答案即可;
- 因子都是成对出现的,所以把 n u m num num 写成 n u m = x ⋅ y ( x ≤ y ) num=x\cdot y(x\le y) num=x⋅y(x≤y) 的形式;
- 枚举 x x x 的范围 [ 1 , n ] [1,\sqrt n] [1,n] ,先特判 x = y x=y x=y 的情况,也就是有一个因子 x x x ,然后对应的 y y y 的取值范围是 [ x + 1 , ⌊ n x ⌋ ] [x+1,\left \lfloor \frac{n}{x} \right \rfloor ] [x+1,⌊xn⌋] ,那么 x x x 因子做 ⌊ n x ⌋ − ( x + 1 ) + 1 \left \lfloor \frac{n}{x} \right \rfloor-(x+1)+1 ⌊xn⌋−(x+1)+1 次贡献,每一个 y y y 做一次贡献;
- 计算 y y y 所作的贡献也用前缀的方法忽略左边界,也就是 [ 1 , ⌊ n x ⌋ ] [1,\left \lfloor \frac{n}{x} \right \rfloor ] [1,⌊xn⌋] 的每个数做一次贡献减掉 [ 1 , x ] [1,x] [1,x] 的每一个数做一次贡献。
代码
#pragma region
//#pragma optimize("Ofast")
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#pragma endregion
const int maxn = 15;
ll getnum(ll x) {
while (x > 9) x /= 10;
return x;
}
void cul(ll y, ll val, ll a[]) {
ll now = 1, cnt = 1;
while (now * 10 <= y + 1) {
rep(i, 1, 9) a[i] += cnt * val;
now *= 10, cnt *= 10;
}
ll tmp = now;
rep(i, 1, 9) {
if (now + tmp <= y + 1) {
a[i] += cnt * val;
now += tmp;
} else {
a[i] += (y - now + 1) * val;
break;
}
}
}
void solve(ll n, ll a[]) {
for (ll x = 1; x * x <= n; ++x) {
a[getnum(x)] += 1 + (n / x - x - 1 + 1);
cul(n / x, 1, a), cul(x + 1 - 1, -1, a);
}
}
ll a[maxn], b[maxn];
int main() {
ll l, r;
scanf("%d%d", &l, &r);
solve(l - 1, a), solve(r, b);
rep(i, 1, 9) printf("%lld\n", b[i] - a[i]);
}