Sticks and Right Triangle HDU - 3939(毕达哥拉斯)

We have a stick with infinite length. Now we want to cut 3 sub-sticks with length x, y, z which is not large than L to form a right triangle. With unknown reasons we assume that x, y, z are all integers and satisfy that x, y, z are all co-primed each other. We want to know how many right triangles are there exist under our constraints
Input
The first line of input is an integer T (T<=5) indicating the number of test cases.
Each case contains a single integer L (L<=1,000,000,000,000).
Output
For each test case output a single integer in one line, indicating the number of right triangles.
Sample Input
1
5
Sample Output
1

Hint
In our test case, we could find a right triangle (3,4,5) which satisfy 3,4,5<=5 and gcd(3,4)=1,gcd(3,5)=1,gcd(4,5)=1.
如果满足构成指教三角形,则有:

a=m2n2m>n
b=2nm
c=n2+m2

使得
a2+b2=c2

先在就从原来的三元组去找二元组即可,
我们看两两互质的条件是什么: m2n2 2nm 互质,则 gcd(m2n2,2nm)=1 ,
因为 m2n2=(mn)2+2nm
所以即只要 gcd(mn)22nm=1 ,进一步即
gcdmn2=1
gcdmnn=1
gcdmnm=1
gcd(n,m)=1mnmn

gcd(n2+m2,2nm)=1
现在看 gcd(m2+n2,m2n2)=1 ,等价于

gcd(m2+n2,mn)=1
gcd(m2+n2,m+n)=1

化为
gcd((mn)2+2nm,mn)=1
gcd((m+n)22nm,m+n)=1
,这个就转化为上面的两个的证明形式了,所以至此,若要满足题意,只需要满足 gcd(n,m)=1,

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 1000005
using namespace std;
bool p[N];
int prime[N];
int phi[N];
int k=0;
void init()//晒出1000000内的素数和欧拉函数
{

    for(int i=1;i<N;i++)
        phi[i]=i;
    for(int i=2;i<N;i++)
    {
        if(!p[i])
        {
            prime[k++]=i;
            for(int j=i;j<N;j+=i)
            {
                p[j]=true;
                phi[j]-=phi[j]/i;
            }
        }
    }
}
int fac[20];
int cnt;
int get(int limit,int x)//1到limit范围和x互质的数量,用容斥
{
    int cnt=0;
    for(int i=0;prime[i]*prime[i]<=x;i++)
    {
        if(x%prime[i]==0)
        {
            fac[cnt++]=prime[i];
            while(x%prime[i]==0)
                x/=prime[i];
        }
        if(x==1)
            break;
    }
    if(x>1)
        fac[cnt++]=x;
    /*cout<<cnt<<endl;
    for(int i=0;i<cnt;i++)
        cout<<fac[i]<<" ";
    cout<<endl;*/
    int ans=0;
    for(int s=1;s<(1<<cnt);s++)
    {
        int mui=1;
        int num=0;
        for(int i=0;i<cnt;i++)
        {
            if((1<<i)&s)
            {
                mui*=fac[i];
                num++;
            }
        }
        //cout<<"mil:"<<mui<<endl;
        //cout<<limit/mui<<endl;
        if(num&1)
            ans+=limit/mui;
        else
            ans-=limit/mui;
    }
    return limit-ans;
}
long long work(long long L)
{
    int up=(int)sqrt(L);
    long long ans=0;
    for(int m=2;m<=up;m++)//枚举m
    {
        int maxx=(int)sqrt(L-(double)m*m);相应的n的最大的取值
        if(m&1)如果m为奇数,偶数n且n与m互质的
        {
            if(m<=maxx)
                ans+=get(m/2,m);
            else
                ans+=get(maxx/2,m);
        }
        else
        {
             if(m<=maxx)
                ans+=phi[m];//这个情况下直接去欧拉函数值
             else
                ans+=get(maxx,m);
        }
    }
    return ans;
}
int main()
{
    init();

    int t;
    long long L;
    //cout<<"ans:"<<get(80,80);
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld",&L);
        printf("%lld\n",work(L));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值