毕达哥拉斯三元组的解

对于一个方程: 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)的查询了。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值