题目描述:
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 holdsX^Z + Y^Z + XYZ = K
where K is another given integer.
Here the operator “^” means power, e.g., 2^3 = 2 * 2 * 2.
Finding a solution is quite easy to Haoren. Now he wants to challenge more: What’s the total number of different solutions?
Surprisingly, he is unable to solve this one. It seems that it’s really a very hard mathematic problem.
Now, it’s your turn.
There are multiple test cases.
For each case, there is only one integer K (0 < K < 2^31) in a line.
K = 0 implies the end of input.
Output
Output the total number of solutions in a line for each test case.
9
53
6
0
1
1
0
Hint
9 = 1^2 + 2^2 + 1 * 2 * 2
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;
}