题目描述:
给定两个整数 l 和 r ,对于所有满足1 ≤ l ≤ x ≤ r ≤ 10^9 的 x ,把 x 的所有约数全部写下来。对于每个写下来的数,只保留最高位的那个数码。求1~9每个数码出现的次数。
解决思路:
- 设计一个calc函数,统计1-x中1~9的数字出现次数。 用2位数打个比方,统计2位数中1出现的次数 10-19枚举for(int i=10; i<=19; ++i) 那么 x/i,就是1-x中含有因数i的个数。通过观察可以发现这个循环在x的位数偏小时计算较快。
- 那么这个数位数较多时?我们可以考虑分块的思想,与上方位数较少时进行互补。我们可以发现 用x/10得到_ed,通过循环变量j枚举[1,_ed];通过[10,19]中每个数乘以j得到的数在x范围内的个数。
代码如下
#include <bits/stdc++.h> using namespace std; #define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0) typedef long long ll; inline int read() { int s = 0, w = 1; char ch = getchar(); while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); } while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar(); return s * w; } ll l, r, a[20], b[20]; vector<ll> p; //p中存放 1 10 100 1000... void init() { ll temp = 1; for (int i = 0; i < 10; ++i) { p.push_back(temp); temp = (temp << 3) + (temp << 1); // *8+*2=*10 } } void calc(ll x, ll* temp) { for (int num = 1; num <= 9; ++num) { //枚举首数字 for (auto i : p) { //基于范围的for遍历容器p if (i < 100000) { // Plan A,位数较少时 ll st = i * num; ll ed = st + i - 1; if (st > x) break; for (ll j = st; j <= ed; ++j) temp[num] += x / j; } else { // Plan B ll st = i * num; ll ed = st + i - 1; if (st > x) break; ll _ed = x / st; for (int j = 1; j <= _ed; ++j) { ll a = x - st * j; temp[num] += min(a / j + 1, ed - st + 1);//j相当于间距 } } } } } int main() { init(); l = read(), r = read(); calc(l - 1, a); calc(r, b); for (int i = 1; i <= 9; ++i) printf("%lld\n", b[i] - a[i]); return 0; }