数码
题目描述
给定两个整数 l 和 r ,对于所有满足1 ≤ l ≤ x ≤ r ≤ 10^9 的 x ,把 x 的所有约数全部写下来。对于每个写下来的数,只保留最高位的那个数码。求1~9每个数码出现的次数。
输入描述:
一行,两个整数 l 和 r (1 ≤ l ≤ r ≤ 10^9)。
输出描述:
输出9行。
第 i 行,输出数码 i 出现的次数。
题意:中文题
思路:首先把问题变为1到l-1和1到r的每个数码出现的次数相减,这样可以不用考虑那么多边界上的问题,
之后对题意进行分析,1e9的数据肯定不可能是O(n)的复杂度只能是O(sqrt(n))或者O(log(n))之类的复杂度,如果只对单个数x来进行分析,可以知道他的约数是成对存在的,(这里暂时不考虑平方的状态),那么就意味着我可以把时间变为O(sqrt(n)),但是我们要求的是1到r范围内所有x的所有约数,这就直接一个O(n),但可以这么想,如果我枚举约数的话不就是O(sqrt(n))了吗,
那么现在进行约数的枚举,在【1,r】范围内,要计算约数x出现的次数,只需要用r/x即可,举个例子,假设x=3,在【1,6】的范围内,约数3可以出现的次数为2,相对应的另外两个约数分别为1,2,如果把范围再扩大一点可以发现例如范围为【1,100】,可以出现33次,另外的约数分别为1到33,(如果仔细一点可以发现,如果每次都这么算的话,会有重复的约数被计算到,x=3时,另外两个约数1和2其实在x=1或2时,已经把3的情况算进去了,所以这里我门要规定一个下界,大于等于x+1),那么现在要进行的是对1到33这些约数进行处理,我们要统计每一个约数最高位的数码出现的次数,这里用到log(n)的复杂度,没有太大影响,也就是对可能出现的约数进行一遍遍历即可。(可能思路写的有点乱,但大体上应该是没有问题的)
参考文章:https://ac.nowcoder.com/discuss/399522
#pragma GCC optimize("Ofast","inline","-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
#define rep(i, a, n) for(int i = a; i < (int)n; i++)
#define per(i, a, n) for(int i = (int)n-1; i >= a; i--)
#define IOS std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define fopen freopen("file.in","r",stdin);freopen("file.out","w",stdout);
#define fclose fclose(stdin);fclose(stdout);
const int inf = 1e9;
const ll onf = 1e18;
const int maxn = 1e5+10;
inline int read(){
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
return x*f;
}
int cal(int x){
while(x>=10) x/=10;
return x;
}
void solve(int x, int *a){
for(int i = 1; i*i <= x; i++){ // 枚举除数的取值
int tmp = x/i; // 得到商的值
for(int j = 1; j <= x; j*=10){ // 这里是对位数的枚举log(n)的复杂度
for(int k = 1; k < 10; k++){
int l = max(i+1, j*k); // 去除重复计算的数,所以商要大于i,因为之前计算i的时候进行了计数,下界
int r = min(tmp, j*(k+1)-1); // 商一定小于等于tmp,上界
if(r>=l) a[k] += r-l+1;
}
}
a[cal(i)] += tmp-i+1; // 计算i的首部使用的次数
}
}
int a[10], b[10];
signed main(){
int l = read(), r = read();
solve(l-1, a);
solve(r, b);
for(int i = 1; i < 10; i++){
b[i] -= a[i];
}
for(int i = 1; i < 10; i++){
printf("%d\n", b[i]);
}
return 0;
}