题目描述
请你对任意的 ccc,c∈N∗c \in \Nu*c∈N∗。求出所有的有序数对 (a,b)(a,b)(a,b) 满足 a2+b2=c2a^2 + b^2 = c^2a2+b2=c2 且 a,b∈N∗a,b \in \Nu*a,b∈N∗。注意,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^7c≤3∗107。
对于 100%100\%100% 的数据,c≤9223372036854775807c \le 9223372036854775807c≤9223372036854775807。
题解
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=c2−b2.
利用平方差公式,a2=(c−b)×(c+b)a ^ 2 = (c - b) \times (c + b)a2=(c−b)×(c+b).
令 x=(c+b)x = (c + b)x=(c+b),y=(c−b)y = (c - b)y=(c−b),则 x+y=2cx + y = 2cx+y=2c,x×y=a2x \times y = a ^ 2x×y=a2.
也就是说,x×yx \times yx×y 为完全平方数。
因此,xgcd(x,y)\frac{x}{\gcd(x, y)}gcd(x,y)x 和 ygcd(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)c−x12,x=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×y,b=x−cb = x - cb=x−c 或 c−yc - yc−y。
#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;
}
由于枚举 iii 从 111 到 2×n2 \times n2×n,时间复杂度仍旧过不了。
3
我们知道,若 aaa 为 bbb 的因数,则 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;
}
珍爱生命,远离抄袭!!
313

被折叠的 条评论
为什么被折叠?



