HDU - 3939 Sticks and Right Triangle —— 毕达哥拉斯三元组

Sticks and Right Triangle

Time Limit: 12000/4000 MS (Java/Others)    Memory Limit: 65535/65536 K (Java/Others)
Total Submission(s): 793    Accepted Submission(s): 314


Problem Description
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
 
 
15
 

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.

题意:给定数l,问能找到多少对a,b,c均小于等于l,使得a^2+b^2=c^2

思路:毕达哥拉斯三元组

a=2mn

b=m^2-n^2

c=m^2+n^2

m>n,二者互素且一奇一偶

①由定理可知,m,n一定<=sqrt(l),所以可以对于每个给定的l,遍历所有的可能m值,求对这个m值有多少n符合题意

②分m为奇数和偶数两种情况

1.当m为偶数时

若m<=n,则n可以取小于m的所有素数,即为m的欧拉函数值,因为与偶数互素的数一定是奇数

若m>n,则只能取小于n并且与m互素的奇数(并不是所有的奇数都与m互素),此时需要把m用算术基本定理分解成多个素数相乘的形式,然后运用容斥求得

2.当m为奇数时

若m<n,则n可以取小于m的所有与m互素的偶数,这样直接用容斥是求不出来的,因为与m互素的数里面包含着奇数和偶数,但有一定理:与奇数互素的偶数的个数等于与其1/2互素数的个数

这样直接运用定理和容斥求与m/2的互素的数的个数即为答案

若m>n,则只能取小于n并且与m互素的偶数,同样运用上面的定理加容斥求得

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <cmath>
#include <vector>
#define max_ 1000010
#define inf 0x3f3f3f3f
#define ll long long
#define les 1e-8
#define mod 9901
using namespace std;
bool vis[max_];
int p[max_];
int prime[max_];
int num,pl;
ll ans,l;
int u[35];
void getprime()
{
    pl=0;
	for(int i=2;i<max_;i++)
	{
		if(vis[i]==false)
		prime[pl++]=i;
        for(int j=0;j<pl&&prime[j]*i<max_;j++)
        {
            vis[i*prime[j]]=true;
            if(i%prime[j]==0)
            break;
        }
	}
}
void getphi()
{
	for(int i=1;i<max_;i++)
	{
		p[i]=i;
		if(i%2==0)
		p[i]>>=1;
	}
	for(int i=3;i<max_;i+=2)
	{
		if(p[i]==i)
		{
			for(int j=i;j<max_;j+=i)
			p[j]-=p[j]/i;
		}
	}
}
void breakprime(int x)
{
	num=0;
	if(vis[x]==false)
	{
		u[num++]=x;
		return;
	}
	for(int i=0;i<pl&&x>1;i++)
	{
		if(x%prime[i]==0)
		{
			u[num++]=prime[i];
			while(x%prime[i]==0)
			{
				x/=prime[i];
			}
			if(x>1&&vis[x]==false)
			{
				u[num++]=x;
				return;
			}
		}
	}
}
void dfs(int x,int r,int s,int n)
{
	if(x==num)
	{
		if(r&1)
		ans-=n/s;
		else
		ans+=n/s;
		return;
	}
	dfs(x+1,r,s,n);
	dfs(x+1,r+1,s*u[x],n);
}
int main(int argc, char const *argv[]) {
	int t;
	scanf("%d",&t);
	getprime();
	getphi();
	while(t--)
	{
		scanf("%lld",&l);
		ans=0;
		int m=(int)sqrt(1.0*l);
		for(int i=m;i>=1;i--)
		{
			int n=(int)sqrt(l-(ll)i*i);
			if(i&1)
			{
				breakprime(i);
				if(i<=n)
					dfs(0,0,1,i>>1);
				else
					dfs(0,0,1,n>>1);
			}
			else
			{
				if(i<=n)
				ans+=p[i];
				else
				{
					breakprime(i);
					dfs(0,0,1,n);
				}
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值