给定两个整数 l 和 r ,对于所有满足1 ≤ l ≤ x ≤ r ≤ 10^9 的 x ,把 x 的所有约数全部写下来。对于每个写下来的数,只保留最高位的那个数码。求1~9每个数码出现的次数。
输入描述:
一行,两个整数 l 和 r (1 ≤ l ≤ r ≤ 10^9)。
输出描述:
输出9行。
第 i 行,输出数码 i 出现的次数。
输入例子1:
1 4
输出例子1:
4
2
1
1
0
0
0
0
0
这道题还是比较好的,一些学校的acm联赛也喜欢出这类题。
直接枚举l-r肯定得超时,那么反过来枚举因数,
对于最高位数位1的来说有 1 10 11 12 … 19 100 101 102 … 199 ….
假设我们求得区间是 1-k 。这样的话答案就是 k/1 + k/10 + k/11 + … k/199。
确实减少了计算量,倒是但枚举到 10000000 的时候就不合适了,再按上述方法枚举次数就太多了。
这个时候分母变得很大,可以利用这个特性来进行分块,例如 k/10000000 与 k/10009999 结果可能都一样(向下取整)。
我们可以将答案都相同的分为一块,这样枚举因子的时候就可以滑动了,从10000000直接滑倒10009999。
但是只能对于起点为1的才可以。求 s-k 这个区间的话 就把 1-s 与 1-k 都求出来相减即可。
实际上说了这么多代码其实很短。
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
vector<ll> work(ll r)
{
vector<ll> ans(10,0);
for(ll u=1;u<=9;u++)
{
for(ll v=1;u*v<=r;v*=10)
{
ll slip,Start=u*v,End=min(u*v+v-1,r);
for(ll i=Start;i<=End;i+=slip)
{
ll mult=r/i,rema=r-(r/i)*i;
slip=1;
slip+=min(rema/mult,End-i);
ans[u]+=(mult*slip);
}
}
}
return ans;
}
int main()
{
ll l,r;
while(scanf("%lld%lld",&l,&r)==2)
{
vector<ll> ans1=work(l-1);
vector<ll> ans2=work(r);
for(int j=1;j<=9;j++)
printf("%lld\n",ans2[j]-ans1[j]);
}
}