题目背景 众所周知,对一元二次方程 ax 2 +bx+c=0(a =0),可以用以下方式求实数解: 计算 Δ=b 2 −4ac,则: 若 Δ<0,则该一元二次方程无实数解。 否则 Δ≥0,此时该一元二次方程有两个实数解 x 1,2 = 2a −b± Δ 。 例如: x 2 +x+1=0 无实数解,因为 Δ=1 2 −4×1×1=−3<0。 x 2 −2x+1=0 有两相等实数解 x 1,2 =1。 x 2 −3x+2=0 有两互异实数解 x 1 =1,x 2 =2。 在题面描述中 a 和 b 的最大公因数使用 gcd(a,b) 表示。例如 12 和 18 的最大公因数是 6,即 gcd(12,18)=6。 题目描述 现在给定一个一元二次方程的系数 a,b,c,其中 a,b,c 均为整数且 a =0。你需要判断一元二次方程 ax 2 +bx+c=0 是否有实数解,并按要求的格式输出。 在本题中输出有理数 v 时须遵循以下规则: 由有理数的定义,存在唯一的两个整数 p 和 q,满足 q>0,gcd(p,q)=1 且 v= q p 。 若 q=1,则输出 {p},否则输出 {p}/{q},其中 {n} 代表整数 n 的值; 例如: 当 v=−0.5 时,p 和 q 的值分别为 −1 和 2,则应输出 -1/2; 当 v=0 时,p 和 q 的值分别为 0 和 1,则应输出 0。 对于方程的求解,分两种情况讨论: 若 Δ=b 2 −4ac<0,则表明方程无实数解,此时你应当输出 NO; 否则 Δ≥0,此时方程有两解(可能相等),记其中较大者为 x,则: 若 x 为有理数,则按有理数的格式输出 x。 否则根据上文公式,x 可以被唯一表示为 x=q 1 +q 2 r 的形式,其中: q 1 ,q 2 为有理数,且 q 2 >0; r 为正整数且 r>1,且不存在正整数 d>1 使 d 2 ∣r(即 r 不应是 d 2 的倍数); 此时: 若 q 1 =0,则按有理数的格式输出 q 1 ,并再输出一个加号 +; 否则跳过这一步输出; 随后: 若 q 2 =1,则输出 sqrt({r}); 否则若 q 2 为整数,则输出 {q2}*sqrt({r}); 否则若 q 3 = q 2 1 为整数,则输出 sqrt({r})/{q3}; 否则可以证明存在唯一整数 c,d 满足 c,d>1,gcd(c,d)=1 且 q 2 = d c ,此时输出 {c}*sqrt({r})/{d}; 上述表示中 {n} 代表整数 {n} 的值,详见样例。 如果方程有实数解,则按要求的格式输出两个实数解中的较大者。否则若方程没有实数解,则输出 NO。 输入格式 输入的第一行包含两个正整数 T,M,分别表示方程数和系数的绝对值上限。 接下来 T 行,每行包含三个整数 a,b,c。 输出格式 输出 T 行,每行包含一个字符串,表示对应询问的答案,格式如题面所述。 每行输出的字符串中间不应包含任何空格。
时间: 2025-07-20 10:20:57 浏览: 4
<think>题目要求求解一元二次方程,并按照特定格式输出最大的实数解。我们需要注意以下几点:
1. 计算判别式delta = b^2 - 4ac。
2. 如果delta<0,输出"NO"。
3. 如果delta>=0,则方程有两个实数解(可能相等),我们需要输出较大的那个解。注意:由于a可能为负数,因此较大的解可能是(-b-sqrt(delta))/(2a)或(-b+sqrt(delta))/(2a)?实际上,当a>0时,较大的解是(-b+sqrt(delta))/(2a);当a<0时,较大的解是(-b-sqrt(delta))/(2a)(因为分母为负,加上sqrt(delta)会使结果更小,所以减去sqrt(delta)的才是较大的解)。但我们可以统一表示为:较大的解为(-b+sqrt(delta))/(2a) 当a>0,而(-b-sqrt(delta))/(2a)当a<0。然而,观察公式:x1 = (-b+sqrt(delta))/(2a), x2=(-b-sqrt(delta))/(2a)。当a>0时,x1>x2;当a<0时,x1<x2。因此,最大解应该是:当a>0时取x1,当a<0时取x2。但是x2可以写成(-b-sqrt(delta))/(2a) = (b+sqrt(delta))/(-2a) [分子分母同时乘以-1],这样分母就为正了。所以我们可以统一处理为:最大解为(-b+sqrt(delta))/(2a)(当a>0)和(b+sqrt(delta))/(-2a)(当a<0)。注意,当a<0时,我们可以写成分子为(b+sqrt(delta)),分母为(-2a)(正数)。这样,我们实际上可以写成:分子为-b+sqrt(delta)(当a>0)或者b+sqrt(delta)(当a<0),分母为2|a|?不对,因为当a<0时,分母是-2a=2|a|,分子是b+sqrt(delta)。所以我们可以这样:
令分子 = (a>0) ? (-b) : b) + sqrt(delta) [注意:sqrt(delta)是正数]
分母 = 2 * abs(a)
但是,这里有一个问题:当a>0时,分子为-b+sqrt(delta);当a<0时,分子为b+sqrt(delta)。这样分母都是正数。
4. 如果sqrt(delta)是整数,那么整个解就是有理数,我们需要将分子和分母化简为最简分数形式输出。
5. 如果sqrt(delta)不是整数,那么解是无理数,我们需要表示为q1 + q2*sqrt(r)的形式,其中q1是有理数部分,q2*sqrt(r)是无理数部分。注意,题目要求将解表示为x=q1+q2*sqrt(r),其中q1,q2为有理数,且q2>0,r是无平方因子的正整数。
如何分解delta?我们需要将delta写成k^2 * r,其中r是无平方因子的(即r>1且不存在d>1使得d^2整除r)。那么,sqrt(delta)=k*sqrt(r)。因此,解可以写成:
[分子部分] = (分子整数部分) + k*sqrt(r) [注意分子整数部分可能是0]
整个解 = [分子部分]/(2|a|) = [分子整数部分/(2|a|)] + [k/(2|a|)] * sqrt(r)
所以,q1 = 分子整数部分/(2|a|) [注意分子整数部分可能是负数]
q2 = k/(2|a|)
然后,我们需要分别化简q1和q2,并按照题目要求输出。
步骤:
1. 计算delta = b*b-4*a*c
如果delta<0,输出"NO"
2. 否则,令sqrt_d_int = isqrt(delta) [即整数平方根,如果delta是完全平方数,则sqrt_d_int*sqrt_d_int==delta]
(1) 如果delta是完全平方数:
分子 = (-b if a>0 else b) + (sqrt_d_int if a>0 else sqrt_d_int) # 注意:这里当a>0时,分子为-b+sqrt_d_int;当a<0时,分子为b+sqrt_d_int(因为此时我们取的是(b+sqrt(delta)),注意b可能是负数)
分母 = 2 * abs(a)
然后化简分子/分母(注意分子可能为0,分母为正),输出最简分数。
(2) 如果delta不是完全平方数:
首先,将delta分解为k^2 * r,其中r无平方因子。方法:令k=1,temp=delta,从2开始循环,如果i*i整除temp,则k乘以i,temp除以i*i,直到不能整除,然后i++,直到i*i>temp。
这样,我们就得到了k和r(r=temp)。
然后,分子整数部分 = -b if a>0 else b [注意:这里我们还没有加上sqrt(delta)的无理部分,所以先记下这个整数部分]
分母 = 2 * abs(a)
现在,整个解的表达式为: [分子整数部分 + k * sqrt(r)] / 分母 = (分子整数部分/分母) + (k/分母) * sqrt(r)
所以,q1 = 分子整数部分/分母,q2 = k/分母。
我们需要分别化简q1和q2。
对于q1:如果分子整数部分不为0,则化简分子整数部分和分母得到p1,q1(即分子整数部分和分母的最大公约数化简),然后输出p1/q1(如果为0则跳过),并加上一个加号(如果p1为正,则直接输出正数,负数会自动带负号,注意题目要求q1!=0才输出,且后面加加号)。
注意:分子整数部分可能是负数,但分母是正数,所以化简时注意符号。
对于q2:它是一个分数k/分母,我们需要化简k和分母(分母是2|a|)得到c和d(即c/d = k/(2|a|)的最简形式),然后按照以下规则输出:
- 如果化简后的q2(即c/d)等于1,则输出"sqrt(r)"
- 如果化简后的分母d=1,则输出"{c}*sqrt({r})"
- 如果化简后的分子c=1,则输出"sqrt({r})/{d}"
- 否则,输出"{c}*sqrt({r})/{d}"
但是,题目中q2的定义是大于0的有理数,我们化简后c和d都是正整数(因为k>0,分母>0),且gcd(c,d)=1。
然而,注意:在分解delta时,我们得到的k是正整数,r是大于1的无平方因子正整数。
但是,这里有一个细节:分子整数部分可能是负数,那么整个解可能是一个负数加上一个正的无理数部分。题目要求输出的是最大解,所以当a<0时,我们取的解是(b+sqrt(delta))/(-2a),而b+sqrt(delta)可能是正也可能是负?实际上,由于sqrt(delta)>=0,当b为正时,b+sqrt(delta)为正;当b为负时,如果|b|>sqrt(delta)那么b+sqrt(delta)为负。但注意,我们要求的是实数解,且delta>=0,所以解存在。但是,题目要求输出的是两个解中较大的一个,而我们在a<0时取的是x2(即(b+sqrt(delta))/(-2a)),由于a<0,所以-2a>0,那么整个解的符号取决于(b+sqrt(delta))。但是,我们之前认为当a<0时,最大解是x2,这个结论是建立在a<0时二次函数开口向下的情况下,所以最大值在对称轴处取得,即两个解中较大的那个是x2(即(-b-sqrt(delta))/(2a)),而我们用(b+sqrt(delta))/(-2a)表示,这个值可能是负数。但是,我们如何保证这个解是较大的?实际上,当a<0时,对称轴x=-b/(2a)处取得最大值,两个解分别位于对称轴两侧,且x2(即(-b-sqrt(delta))/(2a))在对称轴左侧,x1在对称轴右侧。由于开口向下,右侧的解(x1)小于左侧的解(x2)?不对,实际上,因为x1=(-b+sqrt(delta))/(2a)(因为a<0,所以分母为负,所以x1是一个较小的数,而x2=(-b-sqrt(delta))/(2a)分母也是负,分子(-b-sqrt(delta))比(-b+sqrt(delta))更小(因为sqrt(delta)>0,所以-b-sqrt(delta) < -b+sqrt(delta)),而分母为负,所以x2> x1(负数除以负数,分子越小结果越大?不对,我们举个例子:比如a=-1, b=0, c=4,方程-x^2+4=0,解为x=2和x=-2,此时a<0,那么x1=(0+4)/(-2)=-2,x2=(0-4)/(-2)=2,所以x2是较大的解。而我们用(b+sqrt(delta))/(-2a)就是(0+4)/2=2,正确。所以我们的表达式是:分子为b+sqrt(delta),分母为-2a(即2|a|,因为a<0,所以|a|=-a,因此分母为2|a|)。
因此,分子整数部分就是b(当a<0时)或-b(当a>0时),然后加上sqrt(delta)的无理部分。但是,在无理数表示中,我们将其拆成有理部分和无理部分。有理部分就是分子整数部分除以分母(可能为分数),无理部分就是(k/分母)*sqrt(r)。
但是,注意:分子整数部分和k都是整数,分母是2|a|。
例如:方程 -x^2 -3x+2=0,a=-1,b=-3,c=2,delta=(-3)^2-4*(-1)*2=9+8=17。
由于a<0,所以分子为b+sqrt(delta) = -3+sqrt(17),分母为2*1=2。
所以解为(-3+sqrt(17))/2 = -3/2 + (1/2)*sqrt(17) [这里k=1,r=17]
按照题目要求,有理部分q1=-3/2,无理部分q2=1/2,所以输出"-3/2+sqrt(17)/2"。
但是题目要求输出最大解,这里最大解是(-b-sqrt(17))/(2a) = (3-sqrt(17))/(-2) = (sqrt(17)-3)/2,这是一个正数吗?sqrt(17)≈4.123,所以(4.123-3)/2≈0.56,而另一个解(-b+sqrt(17))/(2a)=(3+4.123)/(-2)≈-3.56,所以最大解是0.56,即(sqrt(17)-3)/2。但是我们上面的表达式是(-3+sqrt(17))/2,也就是(sqrt(17)-3)/2,所以正确。
因此,我们按照上述方法拆解。
但是,注意:分子整数部分(-3)是负数,所以有理部分为负,然后加上正的无理部分。
输出格式:如果有理部分不为0,则输出有理部分(化简)并加一个加号,然后输出无理部分。注意,如果有理部分是负数,那么负号会输出,然后加号?不对,有理部分如果是负数,那么整个有理部分输出就是一个负数,然后我们加上一个正的无理部分,所以中间应该是用加号连接吗?例如:-3/2 + (1/2)*sqrt(17) 输出为"-3/2+sqrt(17)/2"。但是,如果有理部分是正数,那么直接输出正有理数加无理部分。
但是,如果有理部分是0,那么我们就只输出无理部分。
另外,无理部分输出时,我们按照题目要求分四种情况。
6. 注意:在化简分数时,要保证分母为正,分子分母互质。
具体实现:
1. 我们需要一个化简分数的函数,返回分子和分母(分母为正,且互质)。
2. 分解delta时,注意效率,因为M最大可能是系数绝对值上限,但delta最大可能是(2*M)^2+...,所以大小在4*M^2*2左右(因为4ac最大为4*M*M,但b^2最大为M^2,所以delta最大为M^2+4*M*M=5*M^2),所以M最大1000,delta最大500万,分解质因数可行。
代码步骤:
对于每个测试用例:
读取a,b,c
计算delta = b*b-4*a*c
如果delta<0: 输出"NO",继续下一个
否则,计算sqrt_d = isqrt(delta) (整数平方根)
如果sqrt_d * sqrt_d == delta,则delta为完全平方数:
分子0 = -b if a>0 else b # 注意,这里分子由两部分组成:整数部分和平方根部分,但平方根部分已经是整数,所以分子0+sqrt_d
分子 = 分子0 + sqrt_d # 注意:当a>0时,分子=-b+sqrt_d;当a<0时,分子=b+sqrt_d
分母 = 2 * abs(a)
如果分子==0,输出"0"
否则,化简分子/分母:注意分子可能是负数,分母为正。
用化简函数得到p,q(q>0,gcd(|p|,q)=1)
如果q==1: 输出str(p)
否则:输出f"{p}/{q}"
否则(不是完全平方数):
分解delta:k=1, r=delta, 从i=2开始循环直到i*i<=r:
while r % (i*i) == 0:
k *= i
r //= (i*i)
i += 1
现在,delta = k*k * r
分子整数部分 = -b if a>0 else b # 注意:这个整数部分还没有加上无理部分,但是无理部分我们单独处理
分母 = 2 * abs(a)
有理部分:分子整数部分/分母 -> 化简
无理部分系数:k/分母 -> 化简
注意:整个解可以写成:有理部分 + (无理部分系数)*sqrt(r)
所以,q1 = 有理部分(可能为0),q2 = 无理部分系数(大于0)
(1)如果有理部分不为0(即分子整数部分不为0或者化简后分子不为0):
有理字符串 = 化简后的分数表示(如果分母为1则输出整数形式,否则分数形式)
注意:如果化简后分子为0,那么有理部分为0,此时不输出有理部分和加号。
但是,分子整数部分可能为0,但化简后分子不为0?不会,因为分子整数部分为0,那么有理部分就是0。所以这里我们判断有理部分是否为0,就看化简后的分子是否为0。
具体:p1, q1 = simplify_fraction(分子整数部分, 分母) # 注意,这个函数返回的分数是分子整数部分/分母的最简形式
如果p1==0,那么有理部分为0,则不需要输出有理部分。
否则,如果有理部分为负数,那么输出字符串会带负号,正数则不带正号(除非是正数且后面还有无理部分,我们后面要加加号吗?不,我们直接输出负号,然后无理部分用加号连接?不对,有理部分如果是负数,那么整个表达式就是负的有理部分加上正的无理部分,所以中间应该是用加号连接,因为无理部分系数是正的。例如:-1/2 + sqrt(2)/2,输出"-1/2+sqrt(2)/2"。
但是,有理部分可能是整数或分数,我们按规则输出。
(2)无理部分:我们处理的是系数k0 = k / (2|a|)的化简形式,设化简后的分子为c,分母为d(即c/d,且gcd(c,d)=1,d>0,c>0?因为k>0,分母>0,所以c>0)
注意:我们是对k和分母进行化简,即求g=gcd(k, 分母),然后c0 = k//g, d0=分母//g。
然后分情况:
- 如果d0==1(即分母为1):
如果c0==1: 则输出"sqrt({r})"
否则: 输出"{c0}*sqrt({r})"
- 否则(d0>1):
如果c0==1: 输出"sqrt({r})/{d0}"
否则: 输出"{c0}*sqrt({r})/{d0}"
然后,如果有理部分不为0,则整个字符串为:有理部分的字符串 + '+' + 无理部分的字符串
如果有理部分为0,则整个字符串为无理部分的字符串。
但是,这里有一个问题:我们并没有将有理部分和无理部分的分数一起通分,而是分开化简的。题目要求整个解表示为q1+q2*sqrt(r),其中q1和q2都是有理数。而我们分开化简是合理的,因为有理部分和无理部分独立。
但是,注意:在分解delta后,整个解可以写成:
[分子整数部分 + k * sqrt(r)] / 分母 = (分子整数部分/分母) + (k/分母)*sqrt(r)
所以,我们分别化简两个分数是合理的。
但是,分子整数部分和k是独立的,所以分别与分母化简。
注意:分子整数部分可能是负数,而k是正数,所以无理部分系数是正数。
例如:分子整数部分=-3,分母=2,k=1,则有理部分=-3/2,无理部分系数=1/2。
输出:有理部分化简为-3/2,然后加上"+",然后无理部分输出"sqrt(17)/2"(如果r=17)?不对,应该输出"sqrt(17)/2"?但是按照我们的情况,c0=1,d0=2,所以输出"sqrt(17)/2"。
所以整个字符串为"-3/2+sqrt(17)/2"
但是,如果分子整数部分为正,比如3,那么有理部分化简为3/2,然后输出"3/2+sqrt(17)/2"。
但是,如果分子整数部分为0,比如0,那么有理部分为0,我们就不输出有理部分,直接输出无理部分。
但是,注意:分子整数部分为0,但k/分母可能为1,那么输出"sqrt(17)"。
另外,如果分子整数部分和分母化简后为整数(比如2/1),那么输出"2"。
所以,我们按照这个思路写。
但是,有一个特殊情况:当a<0时,分子整数部分为b,而b可能是负数。例如:a=-1,b=-3,那么分子整数部分为b=-3(负数),然后k=1,那么有理部分=-3/2,无理部分系数=1/2,输出"-3/2+sqrt(17)/2"。
但是,如果b是正数,比如a=-1,b=3,那么分子整数部分=3,有理部分=3/2,输出"3/2+sqrt(17)/2"。
所以,我们不需要特别处理符号,因为分子整数部分可以是负数。
但是,在分解delta的时候,我们得到的k是正整数,r是无平方因子正整数。
另外,注意:当分子整数部分为0时,我们只输出无理部分,但是无理部分系数化简后可能为0吗?不可能,因为k>=1,分母>=1,所以无理部分系数>0。
还有,在化简有理部分时,分子整数部分为0,则化简后分子为0,分母为1,我们判断分子为0则跳过。
但是,注意:在化简分数函数中,分子为0时,我们返回(0,1)(分母为1)。
所以,我们判断有理部分:如果分子(化简后)为0,则有理字符串为空,且不输出加号。
所以,我们可以这样:
有理字符串 = ""
如果有理部分的分子p1不为0:
如果q1==1: 有理字符串 = str(p1)
否则: 有理字符串 = f"{p1}/{q1}"
然后,我们还要在后面加一个加号(因为无理部分系数为正,所以有理部分后面一定是一个加号?)但是,如果p1是负数呢?比如p1=-3,那么有理字符串就是"-3/2",然后我们加上一个加号?不对,因为有理部分后面是无理部分,而无理部分系数是正数,所以整个表达式是负的有理部分加上正的无理部分,所以中间应该是加号。但是,有理部分本身已经是负数,所以我们直接输出有理字符串,然后后面加一个加号,例如:输出"-3/2+",然后加上无理字符串,这样就是"-3/2+sqrt(17)/2",看起来合理吗?实际上,数学上就是-3/2加上一个正数,所以这样输出是符合数学表达习惯的。但是,如果有理部分是正数,比如"3/2+",然后加上无理部分,也是合理的。
但是,有一种情况:有理部分为负,但无理部分化简后是负数?不可能,因为k>0,分母>0,所以无理部分系数为正。
所以,我们输出的时候,如果有理部分不为0,则输出有理字符串,然后输出一个加号,再输出无理字符串。但是,如果有理部分为负数,那么有理字符串本身已经带有负号,所以输出例如:-1/2+sqrt(2)/2 是合理的。
但是,如果整个解是负数,且无理部分也是?不对,无理部分系数是正的,所以整个解可能为负数(如果有理部分的负值大于无理部分的正值),但输出时我们仍然按照有理部分和无理部分分开。
例如:解为-1.5+0.5,那么就是-1,但是我们这里是无理数,所以不会出现这种情况,因为无理部分有sqrt(r),r>1,所以无理部分至少大于1,所以不会合并为整数。
但是,我们分解delta时,已经将平方因子提取出来,所以sqrt(r)是无理数,因此整个解不会合并为有理数。
所以,我们按照上述方法输出。
但是,注意:在化简无理部分系数时,我们是对k和分母进行化简,而不是对分子整数部分和分母化简。因为分子整数部分和k是独立的。
具体代码:
有理部分化简:
p1, q1 = simplify_fraction(分子整数部分, 分母) # 注意,分子整数部分可能是负数,分母是正数
这里,simplify_fraction函数需要处理分子为负数的情况,并保证返回的分母为正,分子可以是负数。
无理部分系数化简:
我们有一个分数:分子是k,分母是分母(即2|a|),然后化简:
g = gcd(k, 分母)
c0 = k // g
d0 = 分母 // g
然后,c0和d0都是正整数,且互质。
然后按照四种情况构造无理字符串。
最后,组合字符串。
但是,有一个边界:当分子整数部分为0时,我们直接输出无理字符串,不需要加号。
另外,注意:在有理部分化简后,如果p1为0,那么我们就跳过有理部分。
但是,注意:分子整数部分为0,但是可能因为负号?比如a>0时,分子整数部分=-b,如果b=0,那么分子整数部分=0。所以这是合理的。
所以,我们写代码。
注意:在计算分子整数部分时,我们只是取了整数部分(即不含sqrt的部分),所以当a>0时,分子整数部分=-b;当a<0时,分子整数部分=b。然后分母是2|a|。
但是,在无理数解的情况下,整个解就是[分子整数部分 + k*sqrt(r)] / 分母,然后我们拆成两个分数。
因此,代码实现如下:
我们写一个化简分数的函数:
def simplify_fraction(x, y):
# 化简分数x/y,其中y>0,x为整数(可负),返回(p,q)满足p/q=x/y,且gcd(|p|,q)=1,q>0
if x == 0:
return 0, 1
g = gcd(abs(x), y)
x //= g
y //= g
# 如果x为负,则符号放在分子,分母保持正
return x, y
但是,注意:有可能x为正,y为正,化简后分子分母互质。
对于无理部分,我们同样使用这个函数来化简系数,但是注意:无理部分的分子是k(正整数),分母是2|a|(正整数),所以直接化简。
但是,在有理部分,分子整数部分可能是负数,所以用这个函数没问题。
最后,注意:当a<0时,分子整数部分=b,而b可能是负数、正数或0。
另外,我们还需要注意:在分解delta时,循环从2开始,注意效率,因为delta最大5e6,所以循环到sqrt(delta)即可,大约2236次。
但是,注意:有可能delta很大,但分解质因数时,如果遇到大质数,循环次数是O(sqrt(n)),对于5e6,sqrt(5e6)=2236,所以可以接受。
但是,如果T很大(比如1000),那么1000*2236=2.236e6,在Python中可能有点慢,但应该可以接受。
或者,我们可以优化分解:在循环中,每次i增加后,如果i*i>temp,就跳出循环。
代码实现分解:
k = 1
temp = delta
i = 2
while i * i <= temp:
while temp % (i*i) == 0:
k *= i
temp //= (i*i)
i += 1
r = temp
这样,我们就得到了k和r。
然后,分子整数部分 = -b if a>0 else b
分母 = 2 * abs(a)
有理部分:分子整数部分/分母 -> 化简
无理部分系数:k/分母 -> 化简
然后按照上述规则输出。
但是,有一个细节:题目要求输出的是最大解,而我们这样表示就是最大解。
最后,我们输出字符串。
注意:题目要求输出中间不能有空格。
我们写代码时,注意处理分子整数部分为0的情况,以及有理部分化简后为0的情况。
另外,在化简分数时,分子为0的情况单独处理(返回0,1)。
我们还需要一个gcd函数。
由于题目中M最大1000,但a,b,c的绝对值上限为M,所以delta最大为b^2-4ac,b最大M,a和c最大M,所以delta最大为M^2+4*M*M=5*M^2=5e6,所以整数平方根和分解都在可接受范围内。
代码实现:
使用math.isqrt(delta) (Python 3.8+)
如果使用Python版本低于3.8,则可以用int(math.sqrt(delta)),然后判断这个整数的平方是否等于delta。
我们假设环境是Python3.8以上。
所以,代码实现如下:
注意:我们读入T和M,然后T行。
我们用一个列表存储结果,最后一起输出。
下面给出完整代码。
注意:在输出无理部分时,我们得到的r就是分解后无平方因子的部分,所以直接输出。
但是,注意:r可能等于1吗?不可能,因为如果r=1,那么delta=k^2*1=k^2,是完全平方数,所以不会进入无理数分支。所以r>=2。
所以,我们不需要担心r=1的情况。
另外,在化简分数时,注意分子为0的情况。
我们考虑一个例子:a=1,b=0,c=-1 -> x^2-1=0 -> delta=4,完全平方数,所以输出有理数:分子整数部分=-0(因为a>0,所以分子=-0+2=2,分母=2,所以1)->输出"1"。
另一个例子:a=1,b=0,c=-2 -> delta=8,不是完全平方数,分解:8=2^2*2,所以k=2, r=2。
分子整数部分 = -0=0
分母=2
有理部分:0/2 -> 0,所以不输出有理部分。
无理部分:k=2, 分母=2 -> 2/2=1,所以输出"sqrt(2)"。
再一个例子:a=2,b=0,c=-1 -> 2x^2-1=0 -> delta=4*2=8,同上,但是分母=4(因为2|a|=4),有理部分=0/4=0,无理部分:k=2, 分母=4 -> 2/4=1/2,所以输出"sqrt(2)/2"。
再一个例子:a=-2,b=0,c=1 -> -2x^2+1=0 -> delta=8,a<0,所以分子整数部分=b=0,分母=4,所以有理部分0,无理部分:k=2, 分母=4 -> 1/2,所以输出"sqrt(2)/2"(注意,最大解?因为a<0,所以最大解是正的,所以正确)。
再一个例子:a=1,b=1,c=0 -> x^2+x=0 -> delta=1,完全平方数,分子=-1+1=0(因为a>0,分子=-1+1=0),所以输出0?不对,两个解是0和-1,最大解是0。所以输出0。
但是,我们计算:分子=-b+sqrt(delta) = -1+1=0,分母=2,所以0/2=0,输出0。
所以,正确。
但是,注意:分子整数部分=-b(当a>0)或b(当a<0),然后加上sqrt(delta)(整数),所以整个分子就是整数。
所以,我们按照上述思路实现。
但是,注意:在无理数分支,我们分解delta后,整个解表示为[分子整数部分 + k*sqrt(r)]/(2|a|),然后拆开。但是,分子整数部分可能是负数,而k*sqrt(r)是正数,所以整个解可能是一个负数加上一个正数,但题目要求输出最大解,而我们这样表示就是最大解(因为a>0时取的是加号,a<0时取的是b+sqrt(delta)然后除以正分母,所以是最大解)。
因此,我们写代码。
由于题目要求输出最大解,且按照特定格式,所以我们的逻辑正确。
最后,我们输出结果。
注意:在计算分子整数部分时,我们并没有加上k*sqrt(r)(因为那是无理部分,我们单独处理),所以分子整数部分就是一个整数。
代码实现如下:
注意:在分解delta时,我们使用while循环分解每个质因子的平方。
但是,注意:delta可能很大,但最大5e6,所以分解没有问题。
我们使用math.isqrt,所以需要import math。
另外,注意:delta=0的情况,sqrt_d=0,所以分子=-b(a>0)或b(a<0),然后分母=2|a|,所以输出有理数解。
所以,我们的代码可以处理delta=0的情况。
下面给出完整代码。
注意:在化简分数时,分子为0的情况,我们返回(0,1),然后判断分子为0则跳过有理部分。
在无理部分,我们化简系数时,分子k为正整数,分母为正整数,所以返回的c0为正整数,d0为正整数。
然后,我们按照四种情况输出。
但是,注意:在无理部分,有可能化简后的系数是0吗?不可能,因为k>=1,分母>=1,所以系数至少为1/(2|a|)>0。
所以,我们直接处理。
代码:</think>### 一元二次方程求解的完整解决方案
以下是根据题目要求实现的完整代码,使用Python编写,包含详细的注释和解释。代码严格遵循题目要求的输出格式,并处理了所有边界情况。
```python
import math
def gcd(a, b):
"""计算两个整数的最大公约数(欧几里得算法)"""
if b == 0:
return a
return gcd(b, a % b)
def simplify_fraction(num, denom):
"""
将分数化简为最简形式
返回化简后的分子和分母(分母保持正数,分子可负)
"""
if num == 0:
return 0, 1
# 确保分母为正
if denom < 0:
num = -num
denom = -denom
g = gcd(abs(num), abs(denom))
num //= g
denom //= g
return num, denom
def extract_square_factors(n):
"""
将n分解为k²*r的形式,其中r是无平方因子的正整数
返回(k, r)
"""
if n <= 0:
return 1, n
k = 1
temp = n
# 从最小的质数2开始分解
i = 2
while i * i <= temp:
# 检查i²是否能整除temp
while temp % (i * i) == 0:
k *= i
temp //= (i * i)
i += 1
return k, temp
def format_rational(num, denom):
"""将分数格式化为题目要求的字符串形式"""
if num == 0:
return "0"
if denom == 1:
return str(num)
return f"{num}/{denom}"
def main():
import sys
data = sys.stdin.read().split()
if not data:
return
t = int(data[0])
m = int(data[1])
index = 2
results = []
for _ in range(t):
a = int(data[index])
b = int(data[index+1])
c = int(data[index+2])
index += 3
# 1. 计算判别式delta
delta = b * b - 4 * a * c
# 2. 处理无实数解的情况
if delta < 0:
results.append("NO")
continue
# 3. 计算整数平方根
sqrt_d = math.isqrt(delta)
# 4. 处理完全平方情况(有理数解)
if sqrt_d * sqrt_d == delta:
# 确定分子:a>0时用-b+sqrt_d,a<0时用b+sqrt_d
numerator = -b + sqrt_d if a > 0 else b + sqrt_d
denominator = 2 * abs(a)
# 化简分数
num_simp, denom_simp = simplify_fraction(numerator, denominator)
# 格式化为字符串
if num_simp == 0:
results.append("0")
elif denom_simp == 1:
results.append(str(num_simp))
else:
results.append(f"{num_simp}/{denom_simp}")
# 5. 处理无理数解
else:
# 分解delta为k²*r
k_val, r_val = extract_square_factors(delta)
# 计算分子整数部分和分母
integer_part = -b if a > 0 else b
denominator = 2 * abs(a)
# 处理有理部分(整数部分)
rational_str = ""
if integer_part != 0:
num1, denom1 = simplify_fraction(integer_part, denominator)
if num1 != 0: # 化简后可能为0
rational_str = format_rational(num1, denom1) + "+"
# 处理无理数部分系数
# 计算k_val和分母的最大公约数
g = gcd(k_val, denominator)
k_reduced = k_val // g
denom_reduced = denominator // g
# 根据系数情况格式化无理数部分
if denom_reduced == 1:
if k_reduced == 1:
irrational_str = f"sqrt({r_val})"
else:
irrational_str = f"{k_reduced}*sqrt({r_val})"
else:
if k_reduced == 1:
irrational_str = f"sqrt({r_val})/{denom_reduced}"
else:
irrational_str = f"{k_reduced}*sqrt({r_val})/{denom_reduced}"
# 组合最终结果
if integer_part == 0 or rational_str == "":
results.append(irrational_str)
else:
results.append(rational_str + irrational_str)
# 输出所有结果
for res in results:
print(res)
if __name__ == "__main__":
main()
```
### 代码详细解释
#### 1. 辅助函数
- **`gcd(a, b)`**:使用欧几里得算法计算两个整数的最大公约数
- **`simplify_fraction(num, denom)`**:将分数化简为最简形式,确保分母为正
- **`extract_square_factors(n)`**:将整数分解为 $k^2 \times r$ 的形式,其中 $r$ 是无平方因子的正整数
- **`format_rational(num, denom)`**:将分数格式化为题目要求的字符串形式
#### 2. 主函数逻辑
1. **读取输入**:第一行包含测试用例数 $T$ 和系数上限 $M$,接下来 $T$ 行每行包含三个整数 $a, b, c$
2. **计算判别式**:$\Delta = b^2 - 4ac$
- 若 $\Delta < 0$,输出 "NO"
3. **处理完全平方情况**(有理数解):
- 计算整数平方根 $\sqrt{\Delta}$
- 根据 $a$ 的正负确定分子形式
- 化简分数并按题目要求格式化输出
4. **处理无理数解**:
- 分解 $\Delta = k^2 \times r$($r$ 无平方因子)
- 计算有理部分(整数部分)并化简
- 处理无理数部分系数并按题目要求的4种情况格式化
- 组合有理部分和无理部分
5. **输出结果**:按行输出每个测试用例的答案
#### 3. 关键算法说明
1. **判别式计算**:直接计算 $b^2 - 4ac$
2. **完全平方判断**:使用 `math.isqrt()` 计算整数平方根,验证平方是否等于原数
3. **无理数分解**:从最小质数2开始,循环提取平方因子
4. **分数化简**:使用最大公约数化简分数,确保分母为正
5. **格式化输出**:严格遵循题目要求的4种无理数输出格式
#### 4. 示例测试
- 输入:`1 1000 1 0 -2`(方程 $x^2 - 2 = 0$)
- 输出:`sqrt(2)`
- 输入:`1 1000 1 -3 2`(方程 $x^2 - 3x + 2 = 0$)
- 输出:`2`
- 输入:`1 1000 -1 -3 2`(方程 $-x^2 -3x + 2 = 0$)
- 输出:`-3/2+sqrt(17)/2`
- 输入:`1 1000 2 4 2`(方程 $2x^2 + 4x + 2 = 0$)
- 输出:`-1`
### 复杂度分析
- **时间复杂度**:$O(T \times \sqrt{M})$,其中 $T$ 是测试用例数,$M$ 是系数上限
- 主要开销在无理数解的平方因子分解
- **空间复杂度**:$O(1)$(除输入输出外,使用常数额外空间)
### 边界情况处理
- $a = 0$:题目保证 $a \neq 0$
- $\Delta = 0$:按有理数解处理,输出化简后的分数
- 分子为0:直接输出 "0"
- 分母化简为1:输出整数形式
- 系数为负数:正确处理符号和分数化简
阅读全文