[数学]勾股定理

题目描述

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

输入格式

一行一个整数 c c c

输出格式

第一行一个正整数 n n n 表示方案数。
第二行一个正整数 x x x 表示所有有序数对中 a a a 的异或值。

样例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 = 325 c = 325 c=325
对于另外 30 % 30\% 30% 的数据, c ≤ 3 ∗ 1 0 7 c \le 3*10^7 c3107
对于 100 % 100\% 100% 的数据, c ≤ 9223372036854775807 c \le 9223372036854775807 c9223372036854775807

题解

1

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

枚举 a a a,计算出 b b b,判断是否满足条件。

#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

考虑式子 a 2 + b 2 = c 2 a ^ 2 + b ^ 2 = c ^ 2 a2+b2=c2.
移项得到 a 2 = c 2 − b 2 a ^ 2 = c ^ 2 - b ^ 2 a2=c2b2.
利用平方差公式, a 2 = ( 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 = 2 c x + y = 2c x+y=2c x × y = a 2 x \times y = a ^ 2 x×y=a2.
也就是说, x × y x \times y x×y 为完全平方数。

因此, x gcd ⁡ ( x , y ) \frac{x}{\gcd(x, y)} gcd(x,y)x y gcd ⁡ ( x , y ) \frac{y}{\gcd(x, y)} gcd(x,y)y 也是完全平方数。
x 1 = x gcd ⁡ ( x , y ) x1 = \sqrt{\frac{x}{\gcd(x, y)}} x1=gcd(x,y)x , y 1 = y gcd ⁡ ( x , y ) y1 = \sqrt{\frac{y}{\gcd(x, y)}} y1=gcd(x,y)y .
x 1 2 + y 1 2 = x + y gcd ⁡ ( x , y ) = c gcd ⁡ ( 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) 2 c 2c 2c 的因数。

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

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

在计算过程中,
y 1 = c gcd ⁡ ( x , y ) − x 1 2 y1 = \sqrt{\frac{c}{\gcd(x, y)} - x1 ^ 2} y1=gcd(x,y)cx12 x = x 1 2 × gcd ⁡ ( x , y ) x = x1 ^ 2 \times \gcd(x, y) x=x12×gcd(x,y) y = y 1 2 × gcd ⁡ ( x , y ) y = y1 ^ 2 \times \gcd(x, y) y=y12×gcd(x,y),
a = x × y a = \sqrt{x \times y} a=x×y b = x − c b = x - c b=xc c − y c - y cy

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

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

3

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

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

则我们枚举时枚举到 gcd ⁡ ( x , y ) × gcd ⁡ ( x , y ) ≤ 2 × c \gcd(x, y) \times \gcd(x, y) \le 2 \times c gcd(x,y)×gcd(x,y)2×c 就可以了,内部重复两次,用 gcd ⁡ ( x , y ) \gcd(x, y) gcd(x,y) c gcd ⁡ ( 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;
}

珍爱生命,远离抄袭!!

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值