勾股数
这道题是一对兄弟在几个夜晚里想出来的idea:
弟:哥,这是什么?(指着哥哥远古小本子上的一行字)
哥:哦,那是我初三上课无聊时想的一个“定律”,还没证明,也懒得证明。
弟弟拿出了草稿纸和笔,不到五分钟就证完了。
弟:还挺好证的。
哥:是吗,我不知道。
“定律”原文:
若
则 \(a,b,c\) 为一组勾股数。
简证:将 \(y=x+1\) 代入后面几个式子硬算可得出。
弟:可是它不是强定律捏,看看能不能把这个 \(1\) 消去吧。
哥弟俩花了大概10分钟。
哥:嗯,这个应该没错了。
“强定律”原文:
若
则 \(a,b,c\) 为一组勾股数。
证明方法和“非强定律”的证明方法一致。
哥:诶,那如果给定一条边,根据这个定律可以构造出所有的勾股数吗?
弟:……好像可以,如果这条边是 \(a\) 的话可以暴力枚举 \(x,y\),确定 \(x,y\) 就能确定 \(b\),也就能确定 \(c\) 了。
哥:试一试?
弟弟打开了电脑,噼里啪啦了大概20分钟,验证了其正确性。弟弟兴奋不已。
哥:那如果是斜边呢?
两人沉默了一会。
哥:我知道了,\(b^2\) 挪到 \(c^2\) 那边去,开个根号,\(b\) 用 \(c-n\) 代替,把 \((c-n)^2\) 的括号拆出来,合并一下就能得到 \(\sqrt{2cn-n^2}\),这时候枚举 \(n\) 就可以了。
弟弟在刚刚的代码里添加了几行,证明是正确的,两个人十分高兴。
弟:也就是说,现在我们给出一个 \(n\),就能在 \(O(n)\) 的时间复杂度里弄出来所有包含这条边的勾股数了,对吧。
哥:对。
形式化的题解:
回到问题,先来分析 \(p\) 为 \(a\) 的情况:
令 \(p=a\),我们来将 \(p\) 拆解为 \(x+y=2x+n\) 的形式。
根据定理,将其化作:
要想求出所有可能的勾股数,即我们要 求出 \(b+c\) 的所有可能性,枚举 \(x\) 或 \(n\) 均可。
易知此时 \(n\) 的范围为 \(1\sim p-1\),所以时间复杂度为 \(O(p)\)。
\(p\) 为 \(b\) 的情况同理。
再来分析 \(p\) 为 \(c\) 的情况:
令 \(p=c\),根据定理得:
要想求出所有可能的勾股数,即我们要 求出 \(a\) 的所有可能性,枚举 \(n\) 即可。
易知此时:
所以 \(n\) 的范围为 \(1\sim 2c\),即\(1\sim 2p\),时间复杂度为 \(O(p)\)。
综上,总时间复杂度为 \(O(p)\),带 \(3\) 倍常数,可以通过此题。
#include<cstdio>
#include<cmath>
#define int unsigned long long
int work(int p)
{
int cnt=0,a,b,c,x,y,n;
a=p;
// for(x=1;x<=p-1;x++)//枚举x
// {
// y=a-x;
// n=y-x;
// if(n>0&&(2*x*x)%n==0)
// {
// b=(x*x+y*y)/n-n;
// c=(x*x+y*y)/n;
// cnt++;
// }
// }
for(n=1;n<=p-1;n++)//枚举n
{
if((p-n)%2) continue;
x=(p-n)/2;
y=x+n;
if(x<y&&(2*x*x)%n==0)
{
b=(x*x+y*y)/n-n;
c=(x*x+y*y)/n;
cnt++;
}
}
c=p;
for(n=1;n<2*p;n++)
{
int tmp=2*c*n-n*n;
int b=c-n;
a=sqrt(tmp);
if(a*a==tmp&&c>n&&a<b) cnt++;
}
return cnt;
}
signed main()
{
int t;
scanf("%llu",&t);
while(t--)
{
int p;
scanf("%llu",&p);
printf("%llu\n",work(p));
}
return 0;
}

浙公网安备 33010602011771号