题目描述:
Haoren is very good at solving mathematic problems. Today he is working a problem like this:
Find three positive integers X, Y and Z (X < Y, Z > 1) that holdsNow, it’s your turn.
Output the total number of solutions in a line for each test case.
0
53 = 2^3 + 3^3 + 2 * 3 * 3
思路分析:
用二分法解决
题意:
给你一个式子 x^z + y^z + x*y*z = k,k 为给定的某个 int 范围内的数字。
求共有多少组关于 x,y,z 的解。(0< x < y,z > 1)
解题思路:
这题纠结了2天,我擦。今天终于把错误拍出来了。
观察式子不难发现,显然当 z 越大的时候 x,y 的值越小。
由于 y 最小等于2,所以有 2^z < k,又k < 2^31,所以有 z < 31。
1、首先考虑当 z=2 的时候,式子左边可以化为 (x+y)^2 = k 的形式。
所以当 k 是完全平方数的时候,(x,y) 才有可能有解。
假如 m^2 = k ,问题就相当于求 x+y = m (x < y) 的解的组数。
容易得出,当m为偶数时,解组数为 m/2-1;当m为奇数时,解组数为 (m-1)/2。
2、然后考虑当 z>=3 的时候。
当 z=3 的时候,x,y 可能取到的值最大,而稍加计算可以得出 y 的最大值是1290.xx,设这个值为M。
那么枚举x,z的复杂度变为O(M*30),大概是O(10^4)。
如果再直接枚举y的话,复杂度为O(M^2 *30),大概是O(10^7),略大。(不过也能140MS AC)。
那么有没有好的方法呢?
显然当 x,z 确定后,式子关于 y 是单调递增的,于是可以二分,将复杂度降为O(M*logM*30),大概是O(10^5)。(15MS AC)。
第一次用小号交的,爆了0MS,然后竟然排到了rank1。O(∩_∩)O...
Ps:思路一直都是对的,可是昨天WA了一天。
这种题要注意一些细节。在二分的时候,我 y 的右边界一直取的是当 z = 3的时候的 M。这种贪方便的做法会引发一个问题。
就是当 z 逐渐变大的时候,二分区间中很多的值会溢出long long 的范围,导致判断大小错误。
幸好值溢出时会变负,所以我们可以根据值是否为负来判断是否溢出。若溢出,直接等效于大于。
其实这种方法不科学,后来我才知道值溢出时,只保留后几位,首位不一定为1.
解决办法:可以在myPow函数里加一句判断,如果溢出了int,就直接返回int_max,防超。
还掌握了个小知识,每个数字常量默认类型为可满足他范围的类型。
例如,1<<31会变成负的,溢出 int,因为1和31默认为int类型,从而1<<31也为int类型。可用 (LL)1<<31 或 1LL<<31 解决。
#include<stdio.h> #include<string.h> #include<math.h> #include<stdlib.h> #include<ctype.h> int main() { int k,maxn,x,y,z,r; while(scanf("%d",&k)!=EOF&&k!=0) { int ans = 0; for(z = 2;z <= 31;z++) //因为z是幂,影响着大部分,所以先从z开始判断来缩小范围,优化,且由题可得最大为31 { maxn = pow((double)k,1.0/z); for(x = 1;x <= maxn;x++) { int l = x+1; r = maxn; if((pow(x,z)+pow(l,z)+x*l*z)>k||(pow(x,z)+pow(r,z)+x*r*z)<k) continue; while(l<=r) { int mid = (l+r) / 2; long long res = pow(x,z)+pow(mid,z)+x*mid*z; if(res == k) { ans++; break; } else if(res<k) l = mid +1; else r = mid -1; } } } printf("%d\n",ans); } return 0; }