[BZOJ1041][HAOI2008]圆上的整点(数论)

来源:互联网 发布:王者荣耀知乎 编辑:程序博客网 时间:2024/06/09 23:19

为了考虑方便,把问题视为以下模型:
对于任意的1x<r,求出满足r2x2是完全平方数的x的个数,并把结果加1(坐标轴上的整点)再乘44个象限)。
x的个数仍然是暴力统计,但是此题可以利用r2x2是完全平方数的必要条件,缩小枚举范围。
怎样求出这个必要条件呢?首先,把r2x2化为(r+x)(rx)
思考ab为完全平方数的条件。可以看出,如果ab是完全平方数,那么agcd(a,b)bgcd(a,b)一定也是完全平方数,反过来也一样(因为gcd(a,b)2是完全平方数)。此时设u=agcd(a,b),v=bgcd(a,b),那么uv一定互质。容易推出ab为完全平方数当且仅当u,v都是完全平方数。
再看gcd(r+x,rx)的取值。容易得出,gcd(r+x,rx)=gcd(rx,2x)。设d=gcd(rx,2x)
d|x时,则有gcd(rx,2x)=gcd(rx,x)=gcd(r,x),此时d可以取r的任意约数。
否则有gcd(rx,2x)=2gcd(rx,x)=2gcd(r,x),此时d可以取r的任意约数的两倍。
从上面推出,d=gcd(r+x,rx)的取值范围为r的每一个约数和每一个约数的两倍(注意去重)。
考虑枚举d,则可以用「rxd的值为完全平方数」作为必要条件进行计算。由于rxd只能是完全平方数,所以对于每一个d,只要枚举rd以内的所有完全平方数,就可以求出对应的x值并进行判断了,但要注意:
1、必须要有1x<r才能统计。
2、为避免重复,必须判断是否gcd(r+x,rx)=d
代码:

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;typedef long long ll;const int N = 1e4 + 5;int r, tot; ll Ans, a[N];ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}int main() {    int i, d, S, j, cc = 0; cin >> r; S = sqrt(r);    for (i = 1; i <= S; i++)        if (r % i == 0) {            a[++cc] = i; a[++cc] = 2ll * i;            if (r / i != i) a[++cc] = r / i,                a[++cc] = 2ll * (r / i);        }    sort(a + 1, a + cc + 1); tot = unique(a + 1, a + cc + 1) - a - 1;    for (j = 1; j <= tot; j++) {        d = a[j]; for (i = 1; 1ll * i * i * d <= r; i++) {            int x = r - i * i * d; if (x <= 0 || x >= r) continue;            if (gcd(1ll * r + x, 1ll * r - x) != d) continue;            ll w = 1ll * r * r - 1ll * x * x;            ll s = sqrt(w); if (s * s == w) Ans++;        }    }    cout << (Ans + 1) * 4 << endl;    return 0;}