勾股数

这道题是一对兄弟在几个夜晚里想出来的idea:

【哥:KoSaking,弟:osfly

弟:哥,这是什么?(指着哥哥远古小本子上的一行字)

哥:哦,那是我初三上课无聊时想的一个“定律”,还没证明,也懒得证明。

弟弟拿出了草稿纸和笔,不到五分钟就证完了。

弟:还挺好证的。

哥:是吗,我不知道。


“定律”原文:

\[\begin{aligned} &y=x+1\\ &a=x+y\\ &b=x^2+y^2-1\\ &c=x^2+y^2\\ \end{aligned} \]

\(a,b,c\) 为一组勾股数。

简证:将 \(y=x+1\) 代入后面几个式子硬算可得出。


弟:可是它不是强定律捏,看看能不能把这个 \(1\) 消去吧。

哥弟俩花了大概10分钟。

哥:嗯,这个应该没错了。


“强定律”原文:

\[\begin{aligned} &y=x+n\quad(n\ |\ 2x^2\ ,\ \{x,y,n\} \in \mathbb{N} )\\ &a=x+y\\ &b=\frac{x^2+y^2}{n}-n\\ &c=\frac{x^2+y^2}{n}\\ \end{aligned} \]

\(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\) 的形式。

\[\begin{aligned} &a^2+b^2=c^2\\ &a^2=c^2-b^2\\ &a^2=(c+b)(c-b)\\ \end{aligned} \]

根据定理,将其化作:

\[\begin{aligned} &a^2=n(b+c)\\ &b+c=\frac{a^2}{n}\\ \end{aligned} \]

要想求出所有可能的勾股数,即我们要 求出 \(b+c\) 的所有可能性,枚举 \(x\)\(n\) 均可。

易知此时 \(n\) 的范围为 \(1\sim p-1\),所以时间复杂度为 \(O(p)\)

\(p\)\(b\) 的情况同理。

再来分析 \(p\)\(c\) 的情况:

\(p=c\),根据定理得:

\[\begin{aligned} a^2&=c^2-b^2\\ a&=\sqrt{c^2-b^2}\\ &=\sqrt{c^2-(c-n)^2}\\ &=\sqrt{c^2-c^2+2cn-n^2}\\ &=\sqrt{2cn-n^2} \end{aligned} \]

要想求出所有可能的勾股数,即我们要 求出 \(a\) 的所有可能性,枚举 \(n\) 即可。

易知此时:

\[\begin{aligned} 2cn-n^2&>0\\ 2cn&>n^2\\ n&<2c\\\\ \end{aligned} \]

所以 \(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;
}
posted @ 2023-08-25 21:04  osfly  阅读(35)  评论(0)    收藏  举报