码迷,mamicode.com
首页 > 其他好文 > 详细

毕达哥拉斯三元组的解

时间:2015-06-16 17:00:56      阅读:133      评论:0      收藏:0      [点我收藏+]

标签:

对于一个方程: x^2 + y^2 = z^2,他两两互质的正整数解满足一下条件

1. x = 2*m*n ,y = m^2 - n^2 ,z = m^2 + n^2

2. gcd( m , n) =1, m > n , m 与 n 的奇偶性不同

( 2*m*n )^2 + ( m^2 - n^2 )^2 = ( m^2 + n^2 )显然成立。

 

下面我们来看一个例题 POJ 1305 传送门

题意:

给定一个n,然后求满足上面那个方程的解的个数以及不满足勾股数的个数。

分析:

题目的数据范围比较小z^2的范围最大为1e6,因此我们可以直接根据奇偶性来

枚举。

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int maxn = 1e6+10;

int gcd(int a,int b){
    if(b) return gcd(b,a%b);
    return a;
}

int n;
bool vis[maxn];

void solve(){
    memset(vis,0,sizeof(vis));
    int ans1=0,ans2=0;
    for(int i=1;i*i<=n;i++){
        for(int j=2;j*j<=n;j+=2){
            if(gcd(i,j)==1){
                int l=i,r=j;
                if(l>r) swap(l,r);
                if(l*l+r*r<=n){
                    ans1++;
                    vis[2*r*l]=1;
                    vis[r*r-l*l]=1;
                    vis[l*l+r*r]=1;
                }
                int x = 2*l*r;
                int y = -l*l+r*r;
                int z = l*l+r*r;
                for(int k=2;k*z<=n;k++)
                    vis[k*x]=1,vis[k*y]=1,vis[k*z]=1;
            }
        }
    }
    for(int i=1;i<=n;i++)
        if(!vis[i]) ans2++;
    printf("%d %d\n",ans1,ans2);
}

int main()
{
    while(~scanf("%d",&n)){
        solve();
    }
    return 0;
}

 

例题二:HDU3939 Sticks and Right Triangle 传送门

题意:

题意很简单就是求小于n的满足那个方程的解的个数,但是与上题不同的是这题的

数据范围比较大,l<=1e12,如果直接暴力求解的话肯定会超时。

分析:

首先我们先确定一下m,n的大致范围 m<=sqrt(l-1) n <= sqrt(l - m*m) = t。

然后我们还是只能枚举m.(m > n)我们分成以下两种情况来考虑。

1)m为偶数:

    如果 m <= t 那么 n的可能选择就是 phi[m] 表示1~m中与m的互质的数的个数。

    如果 m > t 那么 我们可以对m素因子分解,然后通过容斥原理计算1~t内与m

    互质的数的个数也就是n的可能选择的方案数。

2)m为奇数:

    这种情况下我们需要考虑一下我们需要的是奇偶性与m不相同的且与m互质的n的个

数。

    如果 m <= t 那么 n的可能选择就是区间1~m/2内与m互质的数的个数。

    如果 m  > t 那么 n的可能选择就是 区间1~t/2内与m互质的数的个数。

    因为1~m/2之内的与m互质的数只要乘一个2就转化到了区间1~m且仍与m互质。

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

typedef long long LL;

const int maxn = 1e6+10;

int phi[maxn];
int prime[maxn],cnt,num;
LL ans ;
LL f[100];
bool vis[maxn];

void init(){//筛法求1e6以内的素数和欧拉函数
    cnt = 0;
    memset(vis,0,sizeof(vis));
    for(int i=2;i<maxn;i++){
        if(!vis[i]){
            prime[cnt++]=i;
            for(int j=i+i;j<maxn;j+=i) vis[j]=1;
        }
    }
    for(int i=0;i<maxn;i++)phi[i]=i;
    for(int i=2;i<maxn;i+=2) phi[i]>>=1;
    for(int i=3;i<maxn;i+=2){
        if(phi[i]==i){
            for(int j=i;j<maxn;j+=i)
                phi[j]=phi[j]-phi[j]/i;
        }
    }
}

void get_factor(int x){//对x进行素因子分解
    num=0;
    for(int i=0;i<cnt&&prime[i]*prime[i]<=x;i++){
        if(x%prime[i]==0){
            f[num++]=prime[i];
            while(x%prime[i]==0) x=x/prime[i];
        }
    }
    if(x>1) f[num++]=x;
}

void dfs(int id,int mul,int tot,int x){//容斥原理求[1,x]内与y互质的数的个数f[]为i的素因子
    if(id==num){
        if(tot&1) ans = ans - x/mul;
        else ans = ans + x/mul;
        return;
    }
    dfs(id+1,mul*f[id],tot+1,x);
    dfs(id+1,mul,tot,x);
}

int main()
{
    init();
    int t;
    scanf("%d",&t);
    while(t--){
        LL l;
        scanf("%I64d",&l);
        int n = sqrt(l+0.5);
        ans = 0;
        for(int i=1;i<=n;i++){//枚举m
            int lim = sqrt(l-(LL)i*i+0.5);
            if(i&1){
                get_factor(i);
                if(i<=lim) dfs(0,1,0,i>>1);
                else dfs(0,1,0,lim>>1);
            }
            else{
                if(i<=lim) ans=ans+phi[i];
                else{
                    get_factor(i);
                    dfs(0,1,0,lim);
                }
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

 

这题的代码其实还可以优化,我们可以在预处理的时候将1e6之内的数都直接素因子分解

然后再后来调用的时候就可以 O(1)的查询了。

毕达哥拉斯三元组的解

标签:

原文地址:http://blog.csdn.net/bigbigship/article/details/46517641

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!