![](http://acm.fzu.edu.cn/image/problem.gif)
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