版权声明:转载请注明 https://blog.csdn.net/qq_33831360/article/details/88564226
题目:
给定两个整数l和r,对于任意x,满足l≤x≤r,把x所有约数写下来。
对于每个写下来的数,只保留最高位的那个数码。求[1,9]中每个数码出现的次数.
1≤l≤r≤10^9
送分题?假的吧
首先考虑约数i会被统计r/i-(l-1)/i次,鉴于其形式相同,考虑怎么统计r/i
喜闻乐见的分块
1、当i<sqrt(r)时只有sqrt(r)种情况,可以暴力统计
2、当i>=sqrt(r)时,r/i只有sqrt(r)种情况,可以把出现次数相同的约数统一统计
出现i次的约数是r/(i+1)+1到r/i,是连续的,那这些数有多少以1开头哪?其实可以统计该区间与{[1,1],[10,19],[100,199],[1000,1999]......}的重合部分的数量,这样就可以算了。
总复杂度O(sqrt(n)*log(n))
#include <iostream>
#include <cstdio>
#include <cmath>
typedef long long LL;
using namespace std;
LL cnt[11];
int f(int x) {
while (x >= 10) x /= 10;
return x;
}
void Add(LL l,LL r,int v,int tp) {
LL opr = tp, opl = tp;
while(opl <= r) {
if(opr >= l) cnt[tp] += v*(min(r,opr)-max(l,opl)+1LL);
opl = opl*10;
opr = opr*10+9;
}
}
void calc(int r,int cas) {
int lim = (int)sqrt(r);
for (int i = 1; i <= lim; i++)
for (int j = 1; j <= 9; j++)
Add(r/(i+1)+1,r/i,cas*i,j);
for (int i = r/(lim+1); i >= 1; i--) cnt[f(i)] += cas*r/i;
}
int main() {
int l,r; cin >> l >> r;
calc(r,1); calc(l-1,-1);
for (int i = 1; i <= 9; i++) printf("%lld\n",cnt[i]);
return 0;
}