勾股数

勾股数

题目描述

我国是最早了解勾股定理的国家之一。早在三千多年前,周朝数学家商高就提出,将一根直尺折成一个直角,如果勾等于三、股等于四,那么弦就等于五,即勾三、股四、弦五(注:勾、股、弦分别指不等腰直角三角形中的较短直角边、较长直角边、斜边)它被记载于我国古代著名的数学著作《周髀算经》中。在这本书中的另一处,还记载了勾股定理的一般形式。
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< 
        
          < 
          
         
       
      
      
     
     
    
    
   
   

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值