[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; }