【题解】【模拟】—— [CSP-J 2023] 一元二次方程
- [CSP-J 2023] 一元二次方程
- 题目背景
- 题目描述
- 输入格式
- 输出格式
- 输入输出样例
- 输入 #1
- 输出 #1
- 提示
- 1.题意解析
- 2.AC代码
[CSP-J 2023] 一元二次方程
戳我查看题目(洛谷)
题目背景
众所周知,对一元二次方程 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
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:如果方程有解,那么方程的两个解都是整数。
1.题意解析
这是我第一次自己做出来的大模拟!!!
遇到这种大模拟,思路大体就是将题目分成几个模块。
比如分析这道题,我们可以将它分成三个模块:
1.没有解,即 Δ < 0 \Delta<0 Δ<0;
2.解可以化成有理数,即 Δ \Delta Δ为完全平方数,存在一整数r
,使得 r ∗ r = Δ r*r=\Delta r∗r=Δ;
3.解不可以化成有理数,和上一条相反。
长文警告!!!
先看第一条
这是最好解决的。读入t
组数据,先算出这组数据的
Δ
\Delta
Δ,再判断是否
<
0
<0
<0就行了。示例如下:
while(t--)
{
int a,b,c,delta;
scanf("%d%d%d",&a,&b,&c);
delta=b*b-4*a*c;//求出Δ
if(delta<0)//情况1,Δ小于0,无解
{
printf("NO\n");
continue;
}
}
然后是第二条
首先定义一个函数is_perfect_pow_2_num
,用来判断一个数是不是完全平方数。
bool is_perfect_pow_2_num(int num)//判断是否是完全平方数的函数
{
return pow((int)sqrt(num),2)==num;//隐式类型转换
}
如果看不懂,可以去看看【归纳】常见函数模版和解析中的第四条。
然后判断 Δ \Delta Δ是不是完全平方数,即
if(is_prefect_pow_2_num(delta))
因为题目要求我们求最大值,我们只要化简 x 1 , 2 = − b ± Δ 2 a x _ {1, 2} = \frac{-b \pm \sqrt \Delta}{2a} x1,2=2a−b±Δ,然后求出两者中的较大值就可以了。
为什么不直接取 x = − b + Δ 2 a x=\frac{-b+\sqrt \Delta}{2a} x=2a−b+Δ呢?
看看这一组数据就知道了。
-1 -12 108
// Δ = 576 \Delta=576 Δ=576
输出:-18
。正确答案:6
。
这是因为,如果分母带有负数,那么整个分数也会变成负的。但只要我们让分子也变成负数,上下就抵消了。所以我们为了避免这种情况的发生,会选择两者都算一遍。
那么思路基本就是这样了。但还有两个需要注意的点:
1.分数要化简;
2.分母为 1 1 1不需要输出。
那么定义一个用来化简的函数simplify
,后面需要用到。
void simplify(int &mum,int &son)//将一个分数化简的函数,记得加上&变成应用传参
{
int gcd=__gcd(mum,son);//求出分子分母最大公因数
mum/=gcd;son/=gcd;//化简
if(son<0)son*=-1,mum*=-1;//将-1转移到分子上
}
那么接下来就跟着这个思路去模拟就行了。
if(is_perfect_pow_2_num(delta))//情况2,解能化成有理数
{
//求出两种情况并进行比较
int mum1=-1*b+sqrt(delta),mum2=-1*b-sqrt(delta);
int son1=2*a,son2=2*a;
int mum,son;//储存较大的分数的分母和分子
simplify(mum1,son1);//化简分数1
simplify(mum2,son2);//化简分数2
if(mum1*1.0/son1>mum2*1.0/son2)//取分数值较大的分数
mum=mum1,son=son1;
else
mum=mum2,son=son2;
printf("%d",mum);
if(son!=1)//分子为1,就不输出分子
printf("/%d",son);
puts("");//换行,相当于putchar('\n')或cout<<endl;
}
最后是第三条
对于这种情况,我们将 − b ± Δ 2 a \frac{-b \pm \sqrt \Delta}{2a} 2a−b±Δ拆解为 − b 2 a + ∣ Δ 2 a ∣ \frac{-b}{2a}+|\frac{\sqrt \Delta}{2a}| 2a−b+∣2aΔ∣
这里为什么可以这样做呢?
因为 ± Δ 2 a \pm \frac{\sqrt \Delta}{2a} ±2aΔ化简后为 ± q 2 r ÷ q 3 2 a \pm \frac{q _ 2 \sqrt r \div q_3}{2a} ±2aq2r÷q3,因为是乘除法,所以两者的绝对值是相等的。那么我们就不需要关心它的正负,它如果是正数我们就给它+
,否则给它-
。无论怎样,它最后的值一定是正的,也就一定是最大的。
然后就是如何求 q 2 q_2 q2和 q 3 q_3 q3了。根据算数平方根的运算法则,我们可以将 Δ \sqrt \Delta Δ转换成如下形式: Δ = q 2 r = q 2 ∗ q 2 ∗ r \sqrt \Delta=q_2\sqrt r=\sqrt{q2*q2*r} Δ=q2r=q2∗q2∗r
那么我们就将问题转换成了:求一个最大的
q
2
q_2
q2,使得
q
2
∗
q
2
∗
r
=
Δ
(
r
∈
N
,
即
r
是自然数
)
q2*q2*r=\Delta(r\in\mathbb{N},即r是自然数)
q2∗q2∗r=Δ(r∈N,即r是自然数)。只需要枚举
q
2
q_2
q2,求出
r
r
r就行了。注意:只要枚举到r2*r2<n
就行了。
要注意的是,如果枚举到最后连一个合适的 q 2 q_2 q2都没有,那么就不用进行后面的化简和输出了。
求出
q
2
q_2
q2之后,就使用我们前面分装的simplify
函数化简
q
2
q_2
q2和
q
3
q_3
q3。
最后,如果有合适的 q 2 q_2 q2或 q 3 q_3 q3,那么就输出。记得输出一个换行。
完结撒花!
2.AC代码
#include<bits/stdc++.h>
using namespace std;
bool is_perfect_pow_2_num(int num)//判断是否是完全平方数的函数
{
return pow((int)sqrt(num),2)==num;//隐式类型转换
}
void simplify(int &mum,int &son)//将一个分数化简的函数
{
int gcd=__gcd(mum,son);//求出分子分母最大公因数
mum/=gcd;son/=gcd;//化简
if(son<0)son*=-1,mum*=-1;//将-1转移到分子上
}
int main()
{
int t,m;
scanf("%d%d",&t,&m);
while(t--)
{
int a,b,c,delta;
scanf("%d%d%d",&a,&b,&c);
delta=b*b-4*a*c;//求出Δ
if(delta<0)//情况1,Δ小于0,无解
{
printf("NO\n");
continue;
}
if(is_perfect_pow_2_num(delta))//情况2,解能化成有理数
{
//求出两种情况并进行比较
int mum1=-1*b+sqrt(delta),mum2=-1*b-sqrt(delta);
int son1=2*a,son2=2*a;
int mum,son;//储存较大的分数的分母和分子
simplify(mum1,son1);//化简分数1
simplify(mum2,son2);//化简分数2
if(mum1*1.0/son1>mum2*1.0/son2)//取分数值较大的分数
mum=mum1,son=son1;
else
mum=mum2,son=son2;
printf("%d",mum);
if(son!=1)//分子为1,就不输出分子
printf("/%d",son);
puts("");
}
else//情况3,解只能化成无理数
{
int q1_mum=-1*b,q1_son=2*a,q2,r,q3=2*a;//变量名如题
simplify(q1_mum,q1_son);//化简
for(int i=1;i*i<=delta;i++)//找出最大的q2
{
if(delta%(i*i)!=0)continue;//如果能整除
q2=i;//更新q2
r=delta/(q2*q2);//求出r
}
if(q2*q2<=delta)simplify(q2,q3);//如果有符合条件的q2,化简
q2=abs(q2);//求出绝对值
if(q1_mum*1.0/q1_son!=0)//输出q1
if(q1_son==1)
printf("%d+",q1_mum);
else
printf("%d/%d+",q1_mum,q1_son);
if(q2!=1&&q2*q2<=delta)//输出q2(如果有符合条件的q2的话)
printf("%d*",q2);
printf("sqrt(%d)",r);//输出r
if(q3!=1)//输出q3(如果有符合条件的q3的话)
printf("/%d",q3);
puts("");
}
}
return 0;
}
喜欢就订阅此专辑吧!
【蓝胖子编程教育简介】
蓝胖子编程教育,是一家面向青少年的编程教育平台。平台为全国青少年提供最专业的编程教育服务,包括提供最新最详细的编程相关资讯、最专业的竞赛指导、最合理的课程规划等。本平台利用趣味性和互动性强的教学方式,旨在激发孩子们对编程的兴趣,培养他们的逻辑思维能力和创造力,让孩子们在轻松愉快的氛围中掌握编程知识,为未来科技人才的培养奠定坚实基础。
欢迎扫码关注蓝胖子编程教育