FZU

来源:互联网 发布:电压无功优化的目的 编辑:程序博客网 时间:2024/06/06 20:12
 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 longusing 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 longusing 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