CodeM 数码

一道关于数码出现次数的算法题目,要求统计在给定范围内每个数码出现的次数。通过枚举因数并使用分块策略优化计算,降低时间复杂度。
摘要由CSDN通过智能技术生成

给定两个整数 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]);
        }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值