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

时间:2022-03-01 16:47:24

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 }