[HAOI2008] 圆上的整点

  X2 + Y2 = n2

  X2 = n2 - Y2

  X2 = ( n + Y ) * ( n -Y )

  gcd ( n + Y , n - Y ) = d

  A = ( n + Y ) / d

  B = ( n -  Y ) / d

  X2 = A * B * d2 ( A,B 一定是完全平方数)

  A = a * a

  B = b * b

  A + B = 2 * n / d

  a2 + b2 = 2 * n / d

  对于每一对 ( a , b ) 我们都可以通过推导计算出对应的 ( X , Y )。

  则问题转化为求数对 ( a , b ) 的个数。

  2 * n 的约数个数是 √n 级别的,所以我们可以枚举 d。

  对于一个约数 d ,再枚举 a ,是 d 级别的。

  对每一个 a ,判断整数 b 是否存在,且须满足 gcd ( a *a , b * b ) == 1 && a * a != b * b 。这样才算一对合法的解。

  而注意到,我们枚举的 d 范围 <= √n

  则每一个 d 都有另外一个不同的约数 2 * n / d。( d == 2 * n / d 要特判 )

  按相同方式在进行一次判断即可。

  另需注意:

  1:成立条件中有 gcd ( a *a , b * b ) == 1 ,所以我们枚举出的解一定不包含坐标轴上的4个点。

  2:我们只求 a > 0 ,b > 0 的解,因为对称,将得到的解 * 4 即为四个象限的点对。

  设我们枚举出的数对个数为 tmp 。

  则 ans = tmp * 4 + 4。

  3:我不喜欢对于不必要的数据满篇用 long long ,但这样就要注意中间变量爆精度的问题,这个问题上跪了好几次。

// q.c
// mark迷之精度.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
LL ans;
int r,m,n;
LL gcd(LL a,LL b) {
	return !b?a:gcd(b,a%b);
}
bool check(int a,double b) {
	if(ceil(b)==b) 
		if((LL)a*a!=(LL)(b*b)&&gcd((LL)a*a,(LL)(b*b))==1) return true;
	return false;
}
int main() {
	
	freopen("cir.in","r",stdin);
	freopen("cir.out","w",stdout);
	
	scanf("%d",&r);
	m=(int)sqrt(2.0*(LL)r);
	
	for(int d=1;d<=m;d++) 
	{
		if((2*(LL)r)%d==0) 
		{
			n=(int)sqrt(2.0*r/(2*d));
			for(int a=1;a<=n;a++) 
			{
				double b=sqrt(2.0*r/d-(LL)a*a);
				if(check(a,b)) ans++;
			}
			if(d!=2*r/d) 
			{
				n=(int)sqrt(d/2.0);
				for(int a=1;a<=n;a++) 
				{
					double b=sqrt(d*1.0-(LL)a*a);
					if(check(a,b)) ans++;
				}
			}
		}
	}
	printf("%lld\n",ans*4+4);
	return 0;
}

 

 

posted @ 2018-04-12 08:17  qjs12  阅读(102)  评论(0编辑  收藏  举报