混沌DM

DM Hunter

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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 }
posted on 2012-11-17 19:44  混沌DM  阅读(1384)  评论(0编辑  收藏  举报