luogu2508 [HAOI2008]圆上的整点

题目大意

给出(r),求圆(x^2+y^2=r^2)上坐标均为整数的点数。(n<=2,000,000,000)

总体思路

我们看到这个数据大小,还是个数学题,想到这个的时间复杂度应当为(O(sqrt{r}))。要达到这个效果,我们先要把(r^2)转化成(r),然后在(sqrt{r})的范围内枚举某个数。对于我们以前的经验,这枚举的“某个数”有:质因数分解、求因数等。这个题目好像跟质数的关系不大!那就是枚举因数喽!
以上的叙述就为我们以后的数学推导提供了目标。推导时,应当思维发散,大胆尝试,多尝试几种方法,最终筛选出以下数学推导得出解决办法的过程。

数学推导

经过移项等操作我们得到:

[y^2=(r-x)(r+x) ]

我们令(d=gcd(r+x,r-x))(A=frac{r-x}{d},B=frac{r+x}{d})。这时我们发现:

[A+B=frac{2r}{d} ]

这样,我们在(sqrt{2r})内枚举(d)(同时得到了(d)一个因数和(frac{2r}{d})一个因数),再在(2r/d/2=frac{r}{d})内枚举(A)(B),看看有多少对(A,B)符合要求。这样我们已经把(r)降次了。
但是每枚举一个(d),都需要在(frac{r}{d})内枚举一遍(A),这使时间复杂度近似地变为线性,于我们要求的根号的复杂度仍然有距离。所以我们仍然要进一步优化。

推论1

(a,b,cin Z),若(a^2=b^{2}c),则(sqrt{c}in Z).
证明:(c=(frac{a}{b})^2, b^2|a^2)

推论2

(a,b,cin Z),若(a^2=bc),且(gcd(b,c)=1),则(sqrt{b}in Z, sqrt{c}in Z)
证明:因为(b,c)互质,故根据唯一分解定理,(b,c)的质因数中不存在交集。因为(a)是个完全平方数,组成它的所有质因数的次数都是偶数,而这些质因数都必须存在于(b,c)中,因此原命题成立。

这样,因为(y^2=d^2AB),故根据推论1,(AB)为完全平方数。因为(gcd(A,B)=1),所以根据结论2,(A,B)为完全平方数。所以,为了保证枚举到的(A)都是完全平方数,令(a=sqrt{A},b=sqrt{B}),看看是否能同时满足存在整数(b)使得(a^2+b^2=frac{2r}{d})(gcd(A=a^2,B=b^2)=1)。这样(a)枚举的范围便是(sqrtfrac{r}{d}),进一步加快了速度。

#include <cstdio>
#include <cmath>
using namespace std;

#define ll long long

ll Gcd(ll a, ll b)
{
	return b ? Gcd(b, a%b) : a;
}

void Find(ll r, ll d, ll &ans)
{
	for (ll a = 1; a <= sqrt(r / d); a++)
	{
		ll b = sqrt(r * 2 / d - a * a);
		if (a * a + b * b == r * 2 / d && a != b && Gcd(a * a, b * b) == 1)			ans++;
	}
}

int main()
{
	ll r, ans = 0;
	scanf("%lld", &r);
	for (ll d = 1; d * d <= r * 2; d++)
	{
		if (r * 2 % d == 0)
		{
			Find(r, d, ans);
			if (d*d != r * 2)
				Find(r, r * 2 / d, ans);
		}
	}
	printf("%lld
", ans * 4 + 4);
	return 0;
}
原文地址:https://www.cnblogs.com/headboy2002/p/8955118.html