【题解】【模拟】—— [CSP-J 2023] 一元二次方程

[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 Δ=b24ac,则:
    1. Δ < 0 \Delta < 0 Δ<0,则该一元二次方程无实数解。
    2. 否则 Δ ≥ 0 \Delta \geq 0 Δ0,此时该一元二次方程有两个实数解 x 1 , 2 = − b ± Δ 2 a x _ {1, 2} = \frac{-b \pm \sqrt \Delta}{2a} x1,2=2ab±Δ

例如:

  • 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 Δ=124×1×1=3<0
  • x 2 − 2 x + 1 = 0 x ^ 2 - 2x + 1 = 0 x22x+1=0 有两相等实数解 x 1 , 2 = 1 x _ {1, 2} = 1 x1,2=1
  • x 2 − 3 x + 2 = 0 x ^ 2 - 3x + 2 = 0 x23x+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

对于方程的求解,分两种情况讨论:

  1. Δ = b 2 − 4 a c < 0 \Delta = b ^ 2 - 4ac < 0 Δ=b24ac<0,则表明方程无实数解,此时你应当输出 NO

  2. 否则 Δ ≥ 0 \Delta \geq 0 Δ0,此时方程有两解(可能相等),记其中较大者为 x x x,则:

    1. x x x 为有理数,则按有理数的格式输出 x x x

    2. 否则根据上文公式, 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 d2r(即 r r r 不应是 d 2 d ^ 2 d2 的倍数);

    此时:

    1. q 1 ≠ 0 q _ 1 \neq 0 q1=0,则按有理数的格式输出 q 1 q _ 1 q1,并再输出一个加号 +
    2. 否则跳过这一步输出;

    随后:

    1. q 2 = 1 q _ 2 = 1 q2=1,则输出 sqrt({r})
    2. 否则若 q 2 q _ 2 q2 为整数,则输出 {q2}*sqrt({r})
    3. 否则若 q 3 = 1 q 2 q _ 3 = \frac 1{q _ 2} q3=q21 为整数,则输出 sqrt({r})/{q3}
    4. 否则可以证明存在唯一整数 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.inuqe/uqe2.ans

【数据范围】

对于所有数据有: 1 ≤ T ≤ 5000 1 \leq T \leq 5000 1T5000 1 ≤ M ≤ 1 0 3 1 \leq M \leq 10 ^ 3 1M103 ∣ a ∣ , ∣ b ∣ , ∣ c ∣ ≤ M |a|,|b|,|c| \leq M a,b,cM 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 rr=Δ
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=2ab±Δ ,然后求出两者中的较大值就可以了。

为什么不直接取 x = − b + Δ 2 a x=\frac{-b+\sqrt \Delta}{2a} x=2ab+Δ 呢?
看看这一组数据就知道了。
-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} 2ab±Δ 拆解为 − b 2 a + ∣ Δ 2 a ∣ \frac{-b}{2a}+|\frac{\sqrt \Delta}{2a}| 2ab+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 =q2q2r

    那么我们就将问题转换成了:求一个最大的 q 2 q_2 q2,使得 q 2 ∗ q 2 ∗ r = Δ ( r ∈ N , 即 r 是自然数 ) q2*q2*r=\Delta(r\in\mathbb{N},即r是自然数) q2q2r=Δ(rN,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;
}

喜欢就订阅此专辑吧!

【蓝胖子编程教育简介】
蓝胖子编程教育,是一家面向青少年的编程教育平台。平台为全国青少年提供最专业的编程教育服务,包括提供最新最详细的编程相关资讯、最专业的竞赛指导、最合理的课程规划等。本平台利用趣味性和互动性强的教学方式,旨在激发孩子们对编程的兴趣,培养他们的逻辑思维能力和创造力,让孩子们在轻松愉快的氛围中掌握编程知识,为未来科技人才的培养奠定坚实基础。

欢迎扫码关注蓝胖子编程教育
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝胖子教编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值