uva 106 Fermat vs. Pythagoras

数论经典问题:构造本原勾股数组(PPT):a^2+b^2=c^2 , 其中a,b,c两两互质

题意:给你一个n,让你找出一些勾股数组,a^2+b^2=c^2 , 需要满足a<b<c<=n 。 对于每个case题目首先需要你输出这些勾股数组中PPT的个数,然后再输出一个数字,这个数字是n-所有勾股数组用掉的数字个数

例如:

10

3,4,5  是唯一一组PPT解,所以第一个输出的数字是1

而另外的勾股数组包括 6 8 10 。 那么一共用掉的数字有3,4,5,6,7,8,那么没有用到的数字有4个,所以第二个数字输出4

————————————————————————————————

题意说完了就说怎么解:这个是一个很经典的初等数论问题,在数论概论中第一个问题就是这个问题

勾股数组的个数是无限个,一般的勾股数组并没有太大的研究价值,但是本原勾股数组(PPT)则有,PPT满足a,b,c两两互质,其他的勾股数组都是通过PPT不断翻倍得到的,所以研究本原勾股数组的本质和性质很重要

 

看了那一章然后自己在哦纸上写了一次证明,基本上算是搞懂了,但是不想再在电脑上打一遍,就简单说一下证明的过程

 

1.证明a和b必定一奇一偶,并可以推导出c一定是奇数 。 证明后我们约定a是奇数,b是偶数,c为奇数,但a和b的大小不能确定

2.a^2+b^2=c^2  --->  a^2=c^2-b^2=(c+b)*(c-b)  ,  那么可知(c+b)和(c-b)都是奇数

  然后证明 (c+b)与(c-b)互质,并且两者都是平方数

3.因为(c+b)与(c-b)都是平方数,则

(c+b)=s^2   ;     (c-b)=t^2  ;    ,满足s>t>=1

很容易证明s与t都是奇数,并且两者互质

4.用s和t来表示a,b,c

a=s*t

b=(s*s-t*t)/2

c=(s*s+t*t)/2

有最后得到的这3条式子,我们知道了构建PPT的方法,就是不断枚举两个奇数s和t,只要s和t互质,就可以通过这3条式子得到a,b,c,它们就是一组PPT。得到一组PPT后就不断翻倍得到其他的勾股数组

 

最后回到题目,要满足a<b<c<=n , 所以枚举s和t的时候可以缩小范围,否则会超时,具体实现看代码,注释写得很详细了

 

#include <cstdio>
#include <cmath>
#include <cstring>
#define N 1000010
bool used[N];

long long gcd(long long a , long long b)
{ return b==0 ? a: gcd(b,a%b); }

int main()
{
    long long n,a,b,c;
    long long count1,count2;
    while(scanf("%lld",&n)!=EOF)
    {
        count1=count2=0;
        memset(used,0,sizeof(used));
        long long m=(long long)sqrt(n+0.5);
        for(long long t=1; t<=m; t+=2)  
            for(long long s=t+2; s*t<=n; s+=2)
                if(gcd(s,t)==1)  //s>t>=1且s与t互质
                {
                    a=s*t;          //奇数
                    b=(s*s-t*t)/2;  //偶数
                    c=(s*s+t*t)/2;  //奇数
                    if(c<=n)        //在n范围内的PPT
                    {
                        count1++;      
                        //printf("本原勾股数组:%lld %lld %lld\n",a,b,c);
                        if(!used[a]) { count2++; used[a]=1; }
                        if(!used[b]) { count2++; used[b]=1; }
                        if(!used[c]) { count2++; used[c]=1; }

                        for(int j=2; c*j<=n; j++)  //j是倍数
                        {
                            if(!used[a*j]) { count2++; used[a*j]=1; }
                            if(!used[b*j]) { count2++; used[b*j]=1; }
                            if(!used[c*j]) { count2++; used[c*j]=1; }
                        }
                    }
                }
            printf("%lld %lld\n",count1,n-count2);
    }
    return 0;
}

 

posted @ 2013-01-18 20:52  Titanium  阅读(614)  评论(0编辑  收藏  举报