整数分块练习

小G的约数——牛客练习赛77

题目链接:https://ac.nowcoder.com/acm/contest/11160/C

大意

已知F(n)为n的约数和,G(n)=F(1)+F(2)+…+F(n-1)+F(n)

求G(G(n)).

思路

n最大时易知G(n)=2056198403,常规约数和求法复杂度为O(n),很明显不能用来求G(G(n))。

这时候需要引入整数分块的思路,O( n \sqrt{n} n )的复杂度完全可以胜任。

具体整数分块证明就不做了,说一点可以直接拿来用的东西。

n t = n t ′ \frac{n}{t}=\frac{n}{t'} tn=tn 中,t’的最大取值可通过n/(n/t)得到(就不证了)

另外,通过常规约数和求法我们知道,i因子在1 ~ n范围内的个数为n/i个。通过上式可以得到相同因子个数时的在1 ~ n中的一个范围。

比如n=10时,若t=3,那么t’=3,即[3,3]范围内拥有相同因子个数3;若t=4,那么t’=5,即[4,5]范围内拥有相同因子个数2.

那么,我们既知道了因子个数相同的范围,又知道了该范围内表示因子个数,我们只需要将其全部加起来即可。怎么加也需要一些技巧,暴力累加肯定也是不行的,因为[t,t’]范围可能很大,累加相当于根本没优化。观察一下我们可以知道,因子就是[t,t’]内数本身,比如上面的[4,5],因子个数为2,因子为4、5,我们又知道因子序列为一个等差数列,那么我们只需要对这等差序列求和再乘上因子个数就能得到该部分结果了。

详细可看下面代码

CODE

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1000000007;
ll solve(ll x)
{
    ll ans=0;
    for(ll i=1;i<=x;)
    {
        ll num=x/(x/i);//右边界
        ans+=(x/i)*(i+num)*(num-i+1)/2;//x/i为因子个数,后面为等差数列求和公式
        i=num+1;//跳到右边界下一个点,找下一个范围
    }
    return ans;
}
int main()
{
    ios::sync_with_stdio(0);
    ll n;
    cin>>n;
    cout<<solve(solve(n))<<endl;
    return 0;
}

数码——美团2017年CodeM大赛-资格赛

题目链接:https://ac.nowcoder.com/acm/problem/13221

大意

给定两个整数 l 和 r ,对于所有满足1 ≤ l ≤ x ≤ r ≤ 10^9 的 x ,把 x 的所有约数全部写下来。对于每个写下来的数,只保留最高位的那个数码。求1~9每个数码出现的次数。

思路

每次处理一个数码,每次出现的次数为1 ~ r的约数个数减去1 ~ l-1的约数个数。

每个数码维护一个上限和一个下限,如[1,1],[10,19],[100,199],注意上限不能大于r。

还是一个整数分块,看代码可能更好理解,如下。

CODE

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1000000007;
ll l,r;
ll solve(ll m,ll x)
{
    ll ans=m/x;//最开始为个位,直接求约数个数
    for(ll st=x*10,en=min(m,x*10+9);st<=m;st=st*10,en=min(m,en*10+9))//st为下限,en为上限
    {
        ll j;
        for(ll i=st;i<=en;i=j+1)
        {
            j=min(m/(m/i),en);//注意有界限是可能越界的,判断一下
            ans+=(j-i+1)*(m/i);
        }
    }
    return ans;
}
int main()
{
    ios::sync_with_stdio(0);
    cin>>l>>r;
    for(ll i=1;i<=9;i++)
    {
        cout<<solve(r,i)-solve(l-1,i)<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

第十页

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值