FZU - 2191 完美的数字

 Problem 2191 完美的数字

Accept: 551    Submit: 2035
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

Bob是个很喜欢数字的孩子,现在他正在研究一个与数字相关的题目,我们知道一个数字的完美度是 把这个数字分解成三个整数相乘A*A*B(0<A<=B)的方法数,例如数字80可以分解成1*1*80,2*2*20 ,4*4*5,所以80的完美度是3;数字5只有一种分解方法1*1*5,所以完美度是1,假设数字x的完美度为d(x),现在给定a,b(a<=b),请你帮Bob求出

S,S表示的是从a到b的所有数字的流行度之和,即S=d(a)+d(a+1)+…+d(b)。

 Input

输入两个整数a,b(1<=a<=b<=10^15)

 Output

输出一个整数,表示从a到b的所有数字流行度之和。

 Sample Input

1 80

 Sample Output

107

 Source

福州大学第十二届程序设计竞赛


题意:每个数字都可以拆分成 A*A*B (A<=B) 的形式,而且有时候这种拆分方式不止一种 ,且规定这种拆分方式的数量为该数的 完美度。现在给定两个数 a,b (a<=b) 求a,b之间(包括a,b)所有数字的完美度。

思路一:正向求解 完全暴力。一个循环遍历 [a,b] 对其中每个数分别求完美度
代码:
#include <stdio.h>
#include <math.h>
#include <iostream>
#define ll long long
using namespace std;
ll fd(ll x)
{
    ll i;
    ll sum=0;
    double tmp;
    for(i=1;i*i<=x;i++)
    {
        tmp=x*1.0/(i*i);
        if(tmp==(ll)tmp&&(ll)tmp>=i)
            sum++;
    }
    return sum;
}

int main()
{
    ll a,b,sum;
    while(~scanf("%lld %lld",&a,&b))
    {
        sum=0;
        for(int i=a;i<=b;i++)
        {
            sum+=fd(i);
        }
        printf("%lld\n",sum);
    }
    return 0;
}
但是,最终提交结果显示  Time Limit Exceed 这种解法 还是太暴力了。

思路二:反向求解,提速。在求每一个数字x的完美度时,是将A从1遍历至sqrt(x,1/3.0),若求得的B=x/(A*A)为整数则完美度+1;
现在反向求,将A和B遍历,求出 A B 在不同情况的组合下对应是数字。当 A 取值范围从1到sqrt(x,1/3.0),
B取值范围从1到x/(A*A)时,A*A*B的取值范围则是从 1到 x。随机举些例子找规律:
x=200, A=5,B<=200/(5*5)=8
5*5*1=25
5*5*2=50
5*5*3=75
5*5*4=100
5*5*5=125
5*5*6=150
5*5*7=175
5*5*8=200
由于A<=B 则前面 B=1,2,3,4的情况得舍去
x=100, A=4,B<=100/(4*4)=6
4*4*1=16
4*4*2=32
4*4*3=48
4*4*4=64
4*4*5=80
4*4*6=96
由于A<=B 则前面 B=1,2,3的情况得舍去
由上述可以总结 A在[1,sqrt(x,1/3.0)]区间上变化时,每次A*A*B合法的组合有x/(A*A)-A+1
由于此方法求得的总数为 [1,x]上的总完美度,题目要求是 [a,b]的总完美度,所以可以分别求 [1,b]和[1,a-1]上的总完美度,然后作差。
代码:
#include <stdio.h>
#include <math.h>
#include <iostream>
#define ll long long
using namespace std;
ll fd(ll x)
{
    ll i,sq = pow(x,1/3.0);
    ll sum=0;
    for(i=1;i<=sq;i++)
    {
        sum+=x/(i*i)-i+1;
    }
    return sum;
}

int main()
{
    ll a,b,sum;
    while(~scanf("%I64d %I64d",&a,&b))
    {
        sum=0;
        sum=fd(b)-fd(a-1);
        printf("%I64d\n",sum);
    }
    return 0;
}
注意:题目有一个坑点 就是不支持%lld 输出 long long 必须使用%I64d

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值