\(Description\)
从前有个人,叫好人。他非常擅长解决数学问题。今天他正在做这样的一道数学题:
给定三个正整数 \(X\) , \(Y\) 和\(Z\) \((X < Y, Z > 1)\) 满足
\[ X ^ Z + Y ^ Z + X Y Z = K \]
其中 \(K\) 是另一个给定的正整数。
找出该问题的一个解方案对好人来说是非常简单的事。现在,他想继续挑战一下:该等式总共有多少组不同的解方案呢?
意外的是,这个好人居然做不出来。看来,这是一道难题。现在,是你的 \(show time\) 了。
\(Input\)
输入包含多组数据。
对于每组数据,只有一个整数 \(K\)。
当 \(K = 0\) 时表示输入结束。
\(Output\)
对每组数据,输出一行,仅包含一个整数,表示解方案的个数。
\(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\)
【数据说明】
对于 50% 的数据,\(0 < K < 100000\) 。
对于 100% 的数据,\(0 < K < 2^31\)。
解析
暴力瞎搞,容易想到
考虑先枚举一个 \(z\) ,易知 \(x,y\) 的范围是 \(1\) 到 \(\lfloor \sqrt[z] k \rfloor\)
暴力枚举 \(x\),从 \(1\) 开始,每次加 \(1\)
\(y\) 先指向 \(\lfloor \sqrt[z] k \rfloor\)
把 \(x,y,z\) 代入计算
若值大了,缩小 \(y\);等于记贡献;小于 \(break\),\(x + 1\)
而再进入枚举 \(y\) 时,不需要重新指向 \(\lfloor \sqrt[z] k \rfloor\)
因为 \(x\) 变大了,\(y\) 就不可能比上次大
所以 \(y\) 最多移动 \(\lfloor \sqrt[z] k \rfloor\) 次
因而时间复杂度约莫是:
\[
O(log_2 k·log_2^2k·\sqrt[z] k)
\]
代码
#include<cstdio>
using namespace std;
typedef long long LL;
inline LL fpow(LL x , LL y)
{
LL res = 1;
while (y)
{
if (y & 1) res = res * x;
x = x * x , y >>= 1;
}
return res;
}
inline LL yroot(int x , int y)
{
LL res = 1;
while (fpow(res , y) <= x) res++;
return (fpow(res , y) == x ? res : res - 1);
}
int main()
{
register LL k , ans , _k;
scanf("%d" , &k);
while (k != 0)
{
ans = 0;
for(register LL z = 2; z <= 30; z++)
{
LL l = yroot(k , z) , y = l;
for(register LL x = 1; x <= l; x++)
while (y && y > x)
{
_k = fpow(x , z) + fpow(y , z) + x * y * z;
if (_k == k) ans++;
if (_k <= k) break;
y--;
}
}
printf("%d\n" , ans);
scanf("%d" , &k);
}
}
小巧玲珑,愉快 \(AC\)