文章目录
- 题目
- 题目背景
- 题目描述
- 输入格式
- 输出格式
- 样例 #1
- 样例输入 #1
- 样例输出 #1
- 提示
- 题目传送门
- 题解
- 思路
- 总代码
- 提交结果
- 尾声
题目
题目背景
众所周知,对一元二次方程 a x 2 + b x + c = 0 , ( a ≠ 0 ) ax ^ 2 + bx + c = 0, (a \neq 0) ax2+bx+c=0,(a=0),可以用以下方式求实数解:
- 计算
Δ
=
b
2
−
4
a
c
\Delta = b ^ 2 - 4ac
Δ=b2−4ac,则:
- 若 Δ < 0 \Delta < 0 Δ<0,则该一元二次方程无实数解。
- 否则 Δ ≥ 0 \Delta \geq 0 Δ≥0,此时该一元二次方程有两个实数解 x 1 , 2 = − b ± Δ 2 a x _ {1, 2} = \frac{-b \pm \sqrt \Delta}{2a} x1,2=2a−b±Δ。
例如:
- x 2 + x + 1 = 0 x ^ 2 + x + 1 = 0 x2+x+1=0 无实数解,因为 Δ = 1 2 − 4 × 1 × 1 = − 3 < 0 \Delta = 1 ^ 2 - 4 \times 1 \times 1 = -3 < 0 Δ=12−4×1×1=−3<0。
- x 2 − 2 x + 1 = 0 x ^ 2 - 2x + 1 = 0 x2−2x+1=0 有两相等实数解 x 1 , 2 = 1 x _ {1, 2} = 1 x1,2=1。
- x 2 − 3 x + 2 = 0 x ^ 2 - 3x + 2 = 0 x2−3x+2=0 有两互异实数解 x 1 = 1 , x 2 = 2 x _ 1 = 1, x _ 2 = 2 x1=1,x2=2。
在题面描述中 a a a 和 b b b 的最大公因数使用 gcd ( a , b ) \gcd(a, b) gcd(a,b) 表示。例如 12 12 12 和 18 18 18 的最大公因数是 6 6 6,即 gcd ( 12 , 18 ) = 6 \gcd(12, 18) = 6 gcd(12,18)=6。
题目描述
现在给定一个一元二次方程的系数 a , b , c a, b, c a,b,c,其中 a , b , c a, b, c a,b,c 均为整数且 a ≠ 0 a \neq 0 a=0。你需要判断一元二次方程 a x 2 + b x + c = 0 a x ^ 2 + bx + c = 0 ax2+bx+c=0 是否有实数解,并按要求的格式输出。
在本题中输出有理数 v v v 时须遵循以下规则:
-
由有理数的定义,存在唯一的两个整数 p p p 和 q q q,满足 q > 0 q > 0 q>0, gcd ( p , q ) = 1 \gcd(p, q) = 1 gcd(p,q)=1 且 v = p q v = \frac pq v=qp。
-
若 q = 1 q = 1 q=1,则输出
{p}
,否则输出{p}/{q}
,其中{n}
代表整数 n n n 的值; -
例如:
- 当
v
=
−
0.5
v = -0.5
v=−0.5 时,
p
p
p 和
q
q
q 的值分别为
−
1
-1
−1 和
2
2
2,则应输出
-1/2
; - 当
v
=
0
v = 0
v=0 时,
p
p
p 和
q
q
q 的值分别为
0
0
0 和
1
1
1,则应输出
0
。
- 当
v
=
−
0.5
v = -0.5
v=−0.5 时,
p
p
p 和
q
q
q 的值分别为
−
1
-1
−1 和
2
2
2,则应输出
对于方程的求解,分两种情况讨论:
-
若 Δ = b 2 − 4 a c < 0 \Delta = b ^ 2 - 4ac < 0 Δ=b2−4ac<0,则表明方程无实数解,此时你应当输出
NO
; -
否则 Δ ≥ 0 \Delta \geq 0 Δ≥0,此时方程有两解(可能相等),记其中较大者为 x x x,则:
-
若 x x x 为有理数,则按有理数的格式输出 x x x。
-
否则根据上文公式, x x x 可以被唯一表示为 x = q 1 + q 2 r x = q _ 1 + q _ 2 \sqrt r x=q1+q2r 的形式,其中:
- q 1 , q 2 q _ 1, q _ 2 q1,q2 为有理数,且 q 2 > 0 q _ 2 > 0 q2>0;
- r r r 为正整数且 r > 1 r > 1 r>1,且不存在正整数 d > 1 d > 1 d>1 使 d 2 ∣ r d ^ 2 \mid r d2∣r(即 r r r 不应是 d 2 d ^ 2 d2 的倍数);
此时:
- 若
q
1
≠
0
q _ 1 \neq 0
q1=0,则按有理数的格式输出
q
1
q _ 1
q1,并再输出一个加号
+
; - 否则跳过这一步输出;
随后:
- 若
q
2
=
1
q _ 2 = 1
q2=1,则输出
sqrt({r})
; - 否则若
q
2
q _ 2
q2 为整数,则输出
{q2}*sqrt({r})
; - 否则若
q
3
=
1
q
2
q _ 3 = \frac 1{q _ 2}
q3=q21 为整数,则输出
sqrt({r})/{q3}
; - 否则可以证明存在唯一整数
c
,
d
c, d
c,d 满足
c
,
d
>
1
,
gcd
(
c
,
d
)
=
1
c, d > 1, \gcd(c, d) = 1
c,d>1,gcd(c,d)=1 且
q
2
=
c
d
q _ 2 = \frac cd
q2=dc,此时输出
{c}*sqrt({r})/{d}
;
上述表示中
{n}
代表整数{n}
的值,详见样例。如果方程有实数解,则按要求的格式输出两个实数解中的较大者。否则若方程没有实数解,则输出
NO
。 -
输入格式
输入的第一行包含两个正整数 T , M T, M T,M,分别表示方程数和系数的绝对值上限。
接下来 T T T 行,每行包含三个整数 a , b , c a, b, c a,b,c。
输出格式
输出 T T T 行,每行包含一个字符串,表示对应询问的答案,格式如题面所述。
每行输出的字符串中间不应包含任何空格。
样例 #1
样例输入 #1
9 1000
1 -1 0
-1 -1 -1
1 -2 1
1 5 4
4 4 1
1 0 -432
1 -3 1
2 -4 1
1 7 1
样例输出 #1
1
NO
1
-1
-1/2
12*sqrt(3)
3/2+sqrt(5)/2
1+sqrt(2)/2
-7/2+3*sqrt(5)/2
提示
【样例 #2】
见附件中的 uqe/uqe2.in
与 uqe/uqe2.ans
。
【数据范围】
对于所有数据有: 1 ≤ T ≤ 5000 1 \leq T \leq 5000 1≤T≤5000, 1 ≤ M ≤ 1 0 3 1 \leq M \leq 10 ^ 3 1≤M≤103, ∣ a ∣ , ∣ b ∣ , ∣ c ∣ ≤ M |a|,|b|,|c| \leq M ∣a∣,∣b∣,∣c∣≤M, a ≠ 0 a \neq 0 a=0。
测试点编号 | M ≤ M \leq M≤ | 特殊性质 A | 特殊性质 B | 特殊性质 C |
---|---|---|---|---|
1 1 1 | 1 1 1 | 是 | 是 | 是 |
2 2 2 | 20 20 20 | 否 | 否 | 否 |
3 3 3 | 1 0 3 10 ^ 3 103 | 是 | 否 | 是 |
4 4 4 | 1 0 3 10 ^ 3 103 | 是 | 否 | 否 |
5 5 5 | 1 0 3 10 ^ 3 103 | 否 | 是 | 是 |
6 6 6 | 1 0 3 10 ^ 3 103 | 否 | 是 | 否 |
7 , 8 7, 8 7,8 | 1 0 3 10 ^ 3 103 | 否 | 否 | 是 |
9 , 10 9, 10 9,10 | 1 0 3 10 ^ 3 103 | 否 | 否 | 否 |
其中:
- 特殊性质 A:保证 b = 0 b = 0 b=0;
- 特殊性质 B:保证 c = 0 c = 0 c=0;
- 特殊性质 C:如果方程有解,那么方程的两个解都是整数。
题目传送门
洛谷 P9750 [CSP-J 2023] 一元二次方程
题解
思路
没有任何算法,纯粹的大模拟,细节还蛮多的
由于这道题有多测,所以用一个函数比较好,可以把 a , b , c a,b,c a,b,c 都传进去,这就是主函数
int T, m;
int a, b, c;
int main() {
scanf("%d%d", &T, &m);
while(T-- && scanf("%d%d%d", &a, &b, &c))
work(a, b, c);
return 0;
}
函数里面首先是判断无解,也就是 Δ < 0 \Delta<0 Δ<0,那我们就需要算出 Δ \Delta Δ,即 b 2 − 4 a c b^2-4ac b2−4ac
int delta = b * b - 4 * a * c;
void work(int a, int b, int c) {
delta = b * b - 4 * a * c;
if(delta < 0) {
puts("NO");
return;
}
}
然后需要判断 Δ \Delta Δ 是完全平方数,那么就可以直接算出 − b + Δ 2 a \frac{-b+\sqrt\Delta}{2a} 2a−b+Δ 和 − b − Δ 2 a \frac{-b-\sqrt\Delta}{2a} 2a−b−Δ 哪个大,然后如果能除开就直接输出那个根,否则就输出约分后的那个根
(那个 p r i n t d i v i s i o n ( p , q ) printdivision(p,q) printdivision(p,q) 函数是用来输出 p / q p/q p/q 的,具体请参考注释)
int delta;
double x1, x2;
int sq;
void print_division(int p, int q) {
if(!p) { // 分子为 0,则输出 0
putchar('0');
return;
}
if(p * q < 0) // 两数异号,则输出符号
putchar('-');
if(p < 0) // 将两数都变成正数
p = -p;
if(q < 0)
q = -q;
int g = __gcd(p, q); // 约分
p /= g;
q /= g;
if(q == 1) // 分母为 1,则输出分子
printf("%d", p);
else // 否则输出 “分子/分母”
printf("%d/%d", p, q);
}
void work(int a, int b, int c) {
delta = b * b - 4 * a * c;
if(delta < 0) {
puts("NO");
return;
}
sq = sqrt(delta);
if(sq * sq == delta) {
x1 = 1.0 * (-b + sq) / 2 * a;
x2 = 1.0 * (-b - sq) / 2 * a;
if(x1 > x2)
print_division(-b + sq, 2 * a);
else
print_division(-b - sq, 2 * a);
puts("");
return;
}
}
否则的话就需要按照 “ − b / 2 a + Δ / 2 a -b/2a+\sqrt\Delta/2a −b/2a+Δ/2a” 的格式输出
首先如果 b ≠ 0 b\neq0 b=0,那么就说明 − b / 2 a ≠ 0 -b/2a\neq0 −b/2a=0,就可以输出 “ − b / 2 a + -b/2a+ −b/2a+”
为什么一定是 + + + ?因为如果是 − b − Δ 2 a \frac{-b-\sqrt\Delta}{2a} 2a−b−Δ 更大,那就说明 2 a < 0 2a<0 2a<0,否则不可能 − b − Δ 2 a > − b − Δ 2 a \frac{-b-\sqrt\Delta}{2a}>\frac{-b-\sqrt\Delta}{2a} 2a−b−Δ>2a−b−Δ,所以一定是 + + +
最后就是输出 Δ / 2 a \sqrt\Delta/2a Δ/2a 了,具体怎么做请参考代码注释
int delta;
double x1, x2;
int sq;
void print_division(int p, int q) {
if(!p) { // 分子为 0,则输出 0
putchar('0');
return;
}
if(p * q < 0) // 两数异号,则输出符号
putchar('-');
if(p < 0) // 将两数都变成正数
p = -p;
if(q < 0)
q = -q;
int g = __gcd(p, q); // 约分
p /= g;
q /= g;
if(q == 1) // 分母为 1,则输出分子
printf("%d", p);
else // 否则输出 “分子/分母”
printf("%d/%d", p, q);
}
void print_sqrt(int p, int q) {
if(q < 0) // 如果分母是负数,则将其变为正数,因为和前面的负号消没了(上文说过了)
q = -q;
int u = 1; // 根号前面的系数
for(int i = sqrt(p); i > 1; --i) // 化简
if(!(p % (i * i))) {
p /= i * i;
u *= i;
break;
}
int g = __gcd(u, q); // 约分
u /= g;
q /= g;
if(u > 1) // 系数大于 1,则输出 “系数*”
printf("%d*", u);
if(p > 1) // 根号下的数大于 1,则输出 “sqrt(根号下的数)”
printf("sqrt(%d)", p);
if(q > 1) // 分母大于 1,则输出 “/分母”
printf("/%d", q);
}
void work(int a, int b, int c) {
delta = b * b - 4 * a * c;
if(delta < 0) {
puts("NO");
return;
}
sq = sqrt(delta);
if(sq * sq == delta) {
x1 = 1.0 * (-b + sq) / 2 * a;
x2 = 1.0 * (-b - sq) / 2 * a;
if(x1 > x2)
print_division(-b + sq, 2 * a);
else
print_division(-b - sq, 2 * a);
puts("");
return;
}
if(b) {
print_division(-b, 2 * a);
putchar('+');
}
print_sqrt(delta, 2 * a);
puts("");
}
总代码
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
int T, m;
int a, b, c;
int delta;
double x1, x2;
int sq;
void print_division(int p, int q) {
if(!p) {
putchar('0');
return;
}
if(p * q < 0)
putchar('-');
if(p < 0)
p = -p;
if(q < 0)
q = -q;
int g = __gcd(p, q);
p /= g;
q /= g;
if(q == 1)
printf("%d", p);
else
printf("%d/%d", p, q);
}
void print_sqrt(int p, int q) {
if(q < 0)
q = -q;
int u = 1;
for(int i = sqrt(p); i > 1; --i)
if(!(p % (i * i))) {
p /= i * i;
u *= i;
break;
}
int g = __gcd(u, q);
u /= g;
q /= g;
if(u > 1)
printf("%d*", u);
if(p > 1)
printf("sqrt(%d)", p);
if(q > 1)
printf("/%d", q);
}
void work(int a, int b, int c) {
delta = b * b - 4 * a * c;
if(delta < 0) {
puts("NO");
return;
}
sq = sqrt(delta);
if(sq * sq == delta) {
x1 = 1.0 * (-b + sq) / 2 * a;
x2 = 1.0 * (-b - sq) / 2 * a;
if(x1 > x2)
print_division(-b + sq, 2 * a);
else
print_division(-b - sq, 2 * a);
puts("");
return;
}
if(b) {
print_division(-b, 2 * a);
putchar('+');
}
print_sqrt(delta, 2 * a);
puts("");
}
int main() {
scanf("%d%d", &T, &m);
while(T-- && scanf("%d%d%d", &a, &b, &c))
work(a, b, c);
return 0;
}
提交结果
戳这里看我的提交记录
尾声
如果这篇题解对您(或您的团队)有帮助的话,就帮忙点个赞,加个关注!
最后,祝您(或您的团队)在 OI 的路上一路顺风!!!
┬┴┬┴┤・ω・)ノ Bye~