zoukankan      html  css  js  c++  java
  • codevs 2606 约数和(分块优化数学公式 )

    题目背景

    Smart最近沉迷于对约数的研究中。

    题目描述

    对于一个数X,函数f(X)表示X所有约数的和。例如:f(6)=1+2+3+6=12。对于一个X,Smart可以很快的算出f(X)。现在的问题是,给定两个正整数X,Y(X<Y),Smart希望尽快地算出f(X)+f(X+1)+……+f(Y)的值,你能帮助Smart算出这个值吗?

    输入输出格式

    输入格式:

    输入文件仅一行,两个正整数X和Y(X<Y),表示需要计算f(X)+f(X+1)+……+f(Y)。

    输出格式:

    输出只有一行,为f(X)+f(X+1)+……+f(Y)的值。

    输入输出样例

    输入样例#1: 复制
    2 4
    输出样例#1: 复制
    14
    输入样例#2: 复制
    123 321
    输出样例#2: 复制
    72543

    说明

    对于20%的数据有1≤X<Y≤105。

    对于60%的数据有1≤X<Y≤1*107。

    对于100%的数据有1≤X<Y≤2*109。

    解题思路:首先我们定义一个F(x)=Σf(i)(i=1~x),所表示的是1-x的约数和,那么我们要求的答案即可表示成F(y)-F(x-1),现在的问题是对于任意x怎么求出F(x),我们知道对于任意一个数x在区间[1,n]中将会有n/x个数是x的倍数,所以我们就可以得到F(x)=Σi*(n/i) (i=1-x),我们可以举个例子:

    例如:x=6;

    由题目定义函数f(X)表示X所有约数的和

    f(1)=1;

    f(2)=1+2;

    f(3)=1+3;

    f(4)=1+2+4;

    f(5)=1+5;

    f(6)=1+2+3+6;

    F(6)=f(1)+f(2)+f(3)+f(4)+f(5)+f(6)=1*6+2*3+3*2=4*1+5*1+6*1,我们就可以发现对于每个约数i,其对应的系数为x/i。

    不过我们可以很容易发现这种做法的复杂度为O(y),而y的范围为1≤Y≤2*109,这样做肯定是超时的,所以我们必须想办法对我们的公式进行优化,又由公式F(x)=(⌊x/ii) (其中i=1~x) ,我们经过分析会发现由于x/i向下取整的特性,会导致有连续多个i对应x/i的值相同,我们可以举个例子:

    对于当x=12,F(12)来说:

    约数i:             1        2    3    4    5   6   7    8    9    10    11    12

    对应系数x/i:      12      6    4    3   2   2    1    1    1     1      1      1

    对于刚才的思路我们是直接枚举求和即F(12)=1*12+2*6+3*4+4*3+5*2+6*2+7*1+8*1+9*1+10*1+11*1+12*1,而这必定也会超时的。

    但是由上面列举的情况我们可以发现,对于同一个系数,出现多个i值时,这些i的值时成等差数列的,对于该部分我们可以把它看成是一块,即采用分块思想,该部分我们可以直接采用等差求和公式O(1)便可得出结果,即假设约数i=l到i=r,其对应的系数x/i都为同一个值时,该部分对答案的贡献值为(r-l+1)*(l+r)/2再乘对应权值即x/i;采用这种思路的话,上面的例子我们的计算方法就变成

    F(12)=12*(1-1+1)*(1+1)/2+6*(2-2+1)*(2+2)/2+4*(3-3+1)*(3+3)/2+3*(4-4+1)*(4+4)/2+2*(6-5+1)*(5+6)/2+1*(12-7+1)*(7+12)/2;

    当n很大时,这样便可以大大提高算法的效率了。

    不过对上面公式中出现的l和r怎么求呢?l比较好求,观察上面的数列,每计算一次l就变成上一个r1,初始l=1。

    而对于r,r=x/(x/l);其中x/l表示的是系数,我们需要找到一个最大的数r使得x/r=x/l,即r=x/(x/l),例如例子中l=7时,我们需要找到一个最大的数r使得12/r=12/7,所以r=12/(12/7);

    这样我们的思路就理顺了,

     

    首先初始化l=1,r=0,ans=0,

    计算r的值r=x/(x/l);

    利用等差求和公式求系数为x/i的约束对答案的贡献,即ans+=(x/l)*(r-l+1)*(r+1)/2;

    l=r+1,进入下一块区域。

    代码:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    const LL maxn=1e6+10;
    int n,q,m,r,tot,time;
    LL cal(LL x){
        LL l=1,r=0,ans=0;
        while(l<=x){
            r=x/(x/l);  //计算这块的右边界
            ans+=(x/l)*(r-l+1)*(l+r)/2; //答案加上系数为x/l的块的贡献值 等差求和
            l=r+1;
        }
        return ans;
    }
    int main(){
        LL x,y;
        cin>>x>>y;
        cout<<cal(y)-cal(x-1)<<endl;
        return 0;
    }
  • 相关阅读:
    if __name__
    Python为什么要self
    ubuntu系统中的svn三连
    Python中读取到16进制数如何转成有符号数值
    知网
    Ubuntu 登陆后黑屏 问题解决
    Ubuntu 开机Recovery-Mode,命令行中操作提示 Read-Only File System 只读文件系统的 问题解决
    句子:霓裳虽美始于宫娥之糙手
    VMware 虚拟机 不能打开的解决方案汇总
    Ubuntu 系统安装 数据恢复软件 ext4magic 通过RPM方式
  • 原文地址:https://www.cnblogs.com/zjl192628928/p/10543825.html
Copyright © 2011-2022 走看看