HDU 4473 HDOJ Exam 2012ACM亚洲赛成都赛区J题

http://acm.hdu.edu.cn/showproblem.php?pid=4473

  比赛的时候,这道题我由于思路始终不能从对n开平方深入到正解的对n开三次方,很遗憾没有出。赛后听了题解,恍然大悟。

  以为自己平时思路挺灵活的,现在想想比赛的时候思路还真是僵化啊。

题目意思:

  定义f(x) = 满足(a * b)|x的有序对(a,b)的个数。

  然后输入一个n,求f(1) + f(2) + ... + f(n)

废话不多说,此题的关键在于:

  把原题的条件(a * b)|x 转化为 a * b * y = x

  然后就很好计算了,就是,输入一个n,计算有多少有序对(a, b ,y)满足a*b*y<=n

  不妨设a<=b<=y

  则,a<=n^(1/3) , b<=sqrt(n/a)

  那么

    对于三个数字都相同的情况,只计算一次: i i i

    对于三个数字中有两个相同的情况,计算3次: i i j, i j i, j i i

    对于均不相同的情况,计算6次: a b y ,a y b ,b a y ,b y a, y a b ,y b a

程序如下:注意在HDOJ上使用%I64d,但是现场赛使用%lld

 1 #include<cmath>

 2 #include<cstdio>

 3 using namespace std;

 4 typedef long long ll;

 5 ll pow2(ll n){//高精度开平方

 6     ll m=pow(n,0.5);

 7     while(m*m<=n) m++;

 8     while(m*m>n) m--;

 9     return m;

10 }

11 ll pow3(ll n){//高精度开立方

12     ll m=pow(n,1.0/3);

13     while(m*m*m<=n) m++;

14     while(m*m*m>n) m--;

15     return m;

16 }

17 int main()

18 {

19     int C=0;

20     ll n;

21     while (~scanf("%I64d",&n)){

22         ll m=pow3(n),ans=m;//第一种情况m个

23         for(int i=1;i<=m;i++){

24             ll ni=n/i,k=pow2(ni);//n/i下面多次用到,只计算一次,常数优化

25             ans+=(ni/i + k - i*2)*3;//计算第二种情况,其中i i j有(ni/i - i)个,i j j有(k - i)个

26             for(int j=i+1;j<=k;j++)

27                 ans+=(ni/j - j)*6;//计算第三种情况

28         }

29         printf("Case %d: %I64d\n",++C,ans);

30     }

31     return 0;

32 }

你可能感兴趣的:(2012)