A very hard mathematic problem(二分)

题目描述:   

    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 holds
   X^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.


    Input
  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.


    Sample Input
    9
    53
    6

    0


    Sample Output
    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;
}

你可能感兴趣的:(A very hard mathematic problem(二分))