[数学]勾股定理

题目描述

请你对任意的 cccc∈N∗c \in \Nu*cN。求出所有的有序数对 (a,b)(a,b)(a,b) 满足 a2+b2=c2a^2 + b^2 = c^2a2+b2=c2a,b∈N∗a,b \in \Nu*a,bN。注意,a,b,ca,b,ca,b,c 不一定互质。

输入格式

一行一个整数 ccc

输出格式

第一行一个正整数 nnn 表示方案数。
第二行一个正整数 xxx 表示所有有序数对中 aaa 的异或值。

样例1

输入:

25

输出:

4
4

样例解释:
分别有 (15,20)(15, 20)(15,20),(20,15)(20,15)(20,15),(7,24),(24,7)(7,24),(24,7)(7,24),(24,7) 四组有序序对满足条件。

数据范围

对于 10%10\%10% 的数据,c=325c = 325c=325
对于另外 30%30\%30% 的数据,c≤3∗107c \le 3*10^7c3107
对于 100%100\%100% 的数据,c≤9223372036854775807c \le 9223372036854775807c9223372036854775807

题解

1

首先,我们很容易想到 40%40\%40% 的数据的做法。

枚举 aaa,计算出 bbb,判断是否满足条件。

#define int long long
int n;
int ans = 0, ans2 = -1;//ans 表示答案数,ans2 表示异或值
scanf("%lld", &n);//输入 c
int t = n;//存储 c 的值
n *= n;//c 的平方
int t2 = n / 2;
//由于 a * a + b * b = c * c,所以可以a * a >= c * c / 2 时计算答案,异或和取 a ^ b 的值
for(int i = 1; i < t; ++ i){
	int p = i * i;
	if(p > t2){
		ans *= 2;
		break;
	}
	if(p == t2){
		ans *= 2;
		ans += 1;
		break;
	}
	int q = n - p;//c * c - a * a
	int t1 = sqrt(q);
	if(p + t1 * t1 == n){//说明满足条件
		++ ans;
		if(ans == -1){
			ans2 = i ^ t1;
		}
		else{
			ans2 ^= i ^ t1;
		}
	}
}

2

考虑式子 a2+b2=c2a ^ 2 + b ^ 2 = c ^ 2a2+b2=c2.
移项得到 a2=c2−b2a ^ 2 = c ^ 2 - b ^ 2a2=c2b2.
利用平方差公式,a2=(c−b)×(c+b)a ^ 2 = (c - b) \times (c + b)a2=(cb)×(c+b).

x=(c+b)x = (c + b)x=(c+b),y=(c−b)y = (c - b)y=(cb),则 x+y=2cx + y = 2cx+y=2cx×y=a2x \times y = a ^ 2x×y=a2.
也就是说,x×yx \times yx×y 为完全平方数。

因此,xgcd⁡(x,y)\frac{x}{\gcd(x, y)}gcd(x,y)xygcd⁡(x,y)\frac{y}{\gcd(x, y)}gcd(x,y)y 也是完全平方数。
x1=xgcd⁡(x,y)x1 = \sqrt{\frac{x}{\gcd(x, y)}}x1=gcd(x,y)x,y1=ygcd⁡(x,y)y1 = \sqrt{\frac{y}{\gcd(x, y)}}y1=gcd(x,y)y.
x12+y12=x+ygcd⁡(x,y)=cgcd⁡(x,y)x1 ^ 2 + y1 ^ 2 = \frac{x + y}{\gcd(x,y)} = \frac{c}{\gcd(x, y)}x12+y12=gcd(x,y)x+y=gcd(x,y)c.

gcd⁡(x,y)\gcd(x, y)gcd(x,y)2c2c2c 的因数。

于是我们可以枚举 gcd⁡(x,y)\gcd(x, y)gcd(x,y),再在 2cgcd⁡(x,y)\sqrt{\frac{2c}{\gcd(x, y)}}gcd(x,y)2c 中枚举 x1x1x1,计算 y1y1y1 是否为完全平方数。

大家可以先自己推导一下公式。

在计算过程中,
y1=cgcd⁡(x,y)−x12y1 = \sqrt{\frac{c}{\gcd(x, y)} - x1 ^ 2}y1=gcd(x,y)cx12x=x12×gcd⁡(x,y)x = x1 ^ 2 \times \gcd(x, y)x=x12×gcd(x,y)y=y12×gcd⁡(x,y)y = y1 ^ 2 \times \gcd(x, y)y=y12×gcd(x,y),
a=x×ya = \sqrt{x \times y}a=x×yb=x−cb = x - cb=xcc−yc - ycy

#include<bits/stdc++.h>
using namespace std;
ud n;
ud ans = 0, sum;
map<ud, bool> mp;//标记数组
int main(){
	cin >> n;
	for(ud i = 1; i <= 2 * n; ++ i){
		if((2 * n) % i != 0){
			continue;
		}
		//x1 的范围
		ud t = sqrt(2 * n / i);
		for(ud x = 1; x <= t; ++ x){
			//计算 y1
			ud p = sqrt(2 * n / i - x * x);
			if(p * p + x * x != 2 * n / i || p <= 0){
				continue;
			}
			//这里简化了公式,直接将 x 和 y 去掉
			ud a = x * p * i, b = x * x * i - n;
			if(b <= 0 || a <= 0 || a < b){
				continue;
			}
			//判断是否有重复
			if(mp[a] == 1){
                continue;
			}
			else{
                mp[a] = 1;
			}
			ans += 2;
			if(sum == 0){
				sum = a ^ b;
				continue;
			}
			sum ^= a ^ b;
		}
	}
	//输出
	return 0;
}

由于枚举 iii1112×n2 \times n2×n,时间复杂度仍旧过不了。

3

我们知道,若 aaabbb 的因数,则 b/ab / ab/a 也为 bbb 的因数。

因此我们就可以优化循环,枚举 gcd⁡(x,y)\gcd(x, y)gcd(x,y) 时,由于是 ccc 的因数,因此 cgcd⁡(x,y)\frac{c}{\gcd(x, y)}gcd(x,y)c 也是 ccc 的因数。

则我们枚举时枚举到 gcd⁡(x,y)×gcd⁡(x,y)≤2×c\gcd(x, y) \times \gcd(x, y) \le 2 \times cgcd(x,y)×gcd(x,y)2×c 就可以了,内部重复两次,用 gcd⁡(x,y)\gcd(x, y)gcd(x,y)cgcd⁡(x,y)\frac{c}{\gcd(x, y)}gcd(x,y)c 分别计算一次就行了。

ud n;
ud ans = 0, sum;
map<ud, bool> mp;
int main(){
	for(ud i = 1; i * i <= 2 * n; ++ i){
		if((2 * n) % i != 0){
			continue;
		}
		ud r = 2 * n / i;
		ud t = sqrt(r);
		for(ud x = 1; x * x < r; ++ x){
			ud p = sqrt(r - x * x);
			if(p * p + x * x != r || p <= 0){
				continue;
			}
			ud a = abs(x * p * i), b = abs(n - x * x * i);
			if(a > b){
				swap(a, b);
			}
			if(mp[a] == 1 || a == 0 || b == 0){
				continue;
			}
			else{
				mp[a] = 1;
			}
			ans += 2;
			if(sum == 0){
				sum = a ^ b;
				continue;
			}
			sum ^= a ^ b;
		}
		//如果 i*i 和 2*n 相等,就不用再算一次
		if(2 * n == i * i){
			continue;
		}
		r = i;
		t = sqrt(r);
		for(ud x = 1; x * x < r; ++ x){
			ud p = sqrt(r - x * x);
			if(p * p + x * x != r || p <= 0){
				continue;
			}
			ud a = abs(x * p * 2 * n / i), b = abs(n - x * x * 2 * n / i);
			//这时的 i 已经变为了 2*n/i
			if(a > b){
				swap(a, b);
			}
			if(mp[a] == 1 || a == 0 || b == 0){
				continue;
			}
			else{
				mp[a] = 1;
			}
			ans += 2;
			if(sum == 0){
				sum = a ^ b;
				continue;
			}
			sum ^= a ^ b;
		}
	}
	return 0;
}

珍爱生命,远离抄袭!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值