勾股数
题目描述
我国是最早了解勾股定理的国家之一。早在三千多年前,周朝数学家商高就提出,将一根直尺折成一个直角,如果勾等于三、股等于四,那么弦就等于五,即“勾三、股四、弦五”。(注:勾、股、弦分别指不等腰直角三角形中的较短直角边、较长直角边、斜边)它被记载于我国古代著名的数学著作《周髀算经》中。在这本书中的另一处,还记载了勾股定理的一般形式。
1945年,人们在研究古巴比伦人遗留下的一块数学泥板(普林顿322号)时,惊讶地发现上面竟然刻有15组能构成直角三角形三边的数,其年代远在商高之前。
我们知道直角三角形两条直角边长a,b与斜边长之间满足等式:a² + b² = c² 。我们满足这一等式的数组(a, b, c)称作勾股数组。给定正整数N,请你计算,对于1≤a≤b≤c≤N,能构成多少组勾股数组?
输入格式 2078.in
一个正整数N。
对于30%的测试数据,1≤N≤5 000;
对于70%的测试数据,1≤N≤50 000;
对于100%的测试数据,1≤N≤1 000 000 。
输出格式 2078.out
所求的答案。
输入样例 2078.in
25
输出样例 2078.out
8
解释: 共有8组勾股数组:(3,4,5),(6,8,10),(9,12,15),(12,16,20),(15,20,25),(5,12,13),(7,24,25),(8,15,17)。
这题的解法也是妙,与数学的因式分解有点关联。直接说正解吧,自己的水分说来也没什么意义。
大体思路:
勾股定理是是初二上学期的内容,在直角三角形中a²=c²-b²,由因式分解就可以得出a²=(c+b)(c-b),把c+b假设为y,c-b为x,那么x和y必须要是a的因数。
如果把题目简化为只给a限定范围,而c和b随意,会方便很多。
因此,枚举一重a,并算出a的因数,枚举a的因数,并由a/b可以算出y,最后再用公式b=(y-x)/2,c=(y+x)/2,最后判断一下b和c是否在范围n以内。然后ans++。
分解质因数:
这就要用到分解质因数的方法了,也不知道是以前没学好还是怎么样,听的时候一脸懵逼,后来画了几个数才清楚一些了。
举个例子,求64的因数。64 –> 32 ,32 –> 16,16 –>8 , 8 –> 4 ,4 –>2,2–>1。其中都是除以质数二的关系,数一下,有6次,就是26,便是64分解质因数后的结果。
但在分解质因数前要打一个预处理,算出每个数的最小的质因数,用筛数法。以便在后面快速地得出。
实现:
用一个数组(c)存当前a的质因数有哪些,再开一个数组(num)存次数。
用分解质因数快速求出a的因数:
在枚举a时,用dfs算出因数。假设数组c有两个数,第一个数的次数num为3,便枚举有多少次,用x一直乘上去。
我一开始傻傻地双重for循环,既按顺序累加上去一般,这样会漏掉很多情况。
为什么枚举因数不会超时:
这是一个特别神奇的事情,由wyy的实验得出,10^9的因数也只有100个,因此,n的因数基本是接近log n的。
而用筛数法预处理算出每个数的最小质因数,时间复杂度也只是O(n log log n)或O(n)。
关于某些细节:
1、x不可能超过a,三角形定理,c-b<a,两边之差小于第三边。
2、因为分解质因数算的是n没有平方之前的次数,因此平方以后,要把num乘2。
3、一开始我蠢蠢地把c和num清零,一下子就变成了平方级别,超时抄得死死的。超时错法:用num[i]记录质数i的次数,这样就必须每一次都要清空。正确做法:记下是第几个质数,如果与前面一个数的最小质因数不同,便k++,num[k]=1。而且c的记录同样要判断与前面记录的数是否相同,不然可能会开很多个数组记录的都是同一个数。这样便会有很多重复的情况。
4、为了避免算重,限制a必须要小于c并且小于b。
5、有些要用longlong,不然数字大会出错。
代码如下
#include
#include
#include
#include
using namespace std;
const int maxn=1000005;
long long n;
int k,v[maxn],e,ans,c[maxn],num[maxn];
void init()
{
for(long long i=2;i<=n;i++)
{
if(v[i]==-1)
{
for(long long j=1;i*j<=n;j++)
v[i*j]=i;
}
}
}
void dfs(long long now)
{
k=0,e=0;
long long last=0;
while(now!=1)
{
long long next=v[now];
if(next!=last) c[++e]=next;
if(next!=last) num[++k]=1;
else num[k]++;
last=next;
now=now/v[now];
}
}//分解质因数
void dfs2(long long a,long long x,long long now)
{
if(x>=a) return;
if(now>e)
{
long long aa=a*a;
long long y=aa/x;
if(y%2==x%2)
{
long long cc,bb;
bb=(x+y)/2;
cc=(y-x)/2;
if(cc<=n&&bb<=n&&a
>n; memset(v,-1,sizeof(v)); init(); start(); cout<
<