1、这题是道典型的数论题。数论我没有系统学习过,也证不出别人那样严谨的步骤,只能背一下结论。膜拜!
2、摘抄某位ACMer的解法,真的很不错:
数论经典问题:构造本原勾股数组(PPT):a^2+b^2=c^2 , 其中a,b,c两两互质
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后就不断翻倍得到其他的勾股数组。
#include<cstdio>
#include<cstring>
#include<cmath>
int const N=1000010;
int vis[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)==1){
count1=count2=0;
memset(vis,0,sizeof(vis));
long long m=(long long)sqrt(n+0.5);
for(long long t=1;t<=m;t++)
for(long long s=t+2;s*t<=n;s+=2){
if(gcd(s,t)==1){
a=s*t;
b=(s*s-t*t)/2;
c=(s*s+t*t)/2;
if(c<=n){
count1++;
if(!vis[a]){count2++;vis[a]=1;}
if(!vis[b]){count2++;vis[b]=1;}
if(!vis[c]){count2++;vis[c]=1;}
}
for(int j=2;c*j<=n;j++){
if(!vis[a*j]){count2++;vis[a*j]=1;}
if(!vis[b*j]){count2++;vis[b*j]=1;}
if(!vis[c*j]){count2++;vis[c*j]=1;}
}
}
}
printf("%lld %lld\n",count1,n-count2);
}
return 0;
}