2100:【23CSPJ普及组】一元二次方程(uqe)

【题目描述】
众所周知,对一元二次方程 ax2+bx+c=0,(a≠0)
,可以用以下方式求实数解:


计算 Δ=b2−4ac
,则:

  1. 若 Δ<0
    ,则该一元二次方程无实数解。

  2. 否则 Δ≥0
    ,此时该一元二次方程有两个实数解 x1,2=−b±Δ√2a

例如:

∙x2+x+1=0
无实数解,因为 Δ=12−4×1×1=−3<0

∙x2−2x+1=0
有两相等实数解 x1,2=1

∙x2−3x+2=0
有两互异实数解 x1=1,x2=2

在题面描述中 a
和 b
的最大公因数使用 gcd(a,b)
表示。例如 12
和 18
的最大公因数是 6
,即 gcd(12,18)=6

现在给定一个一元二次方程的系数 a,b,c
,其中 a,b,c
均为整数且 a≠0
。你需要判断一元二次方程 ax2+bx+c=0
是否有实数解,并按要求的格式输出。

在本题中输出有理数 v
时须遵循以下规则:


由有理数的定义,存在唯一的两个整数 p
和 q
,满足 q>0
,gcd(p,q)=1
且 v=pq


若 q=1
,则输出 {p},否则输出 {p}/{q},其中 {n} 代表整数 n
的值;


例如:


当 v=−0.5
时,p
和 q
的值分别为 −1
和 2
,则应输出 -1/2;


当 v=0
时,p
和 q
的值分别为 0
和 1
,则应输出 0。

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

  1. 若 Δ=b2−4ac<0
    ,则表明方程无实数解,此时你应当输出 NO;

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

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

(2). 否则根据上文公式,x
可以被唯一表示为 x=q1+q2r√
的形式,其中:

 ∙q1,q2

为有理数,且 q2>0

 ∙r

为正整数且 r>1
,且不存在正整数 d>1
使 d2∣r
(即 r
不应是 d2
的倍数);

此时:

  1. 若 q1≠0
    ,则按有理数的格式输出 q1
    ,并再输出一个加号 +;

  2. 否则跳过这一步输出;

随后:

  1. 若 q2=1
    ,则输出 sqrt({r});

  2. 否则若 q2
    为整数,则输出 {q2}*sqrt({r});

  3. 否则若 q3=1q2
    为整数,则输出 sqrt({r})/{q3};

  4. 否则可以证明存在唯一整数 c,d
    满足 c,d>1,gcd(c,d)=1
    且 q2=cd
    ,此时输出{c}*sqrt({r})/{d};

上述表示中 {n} 代表整数 {n} 的值,详见样例。

如果方程有实数解,则按要求的格式输出两个实数解中的较大者。否则若方程没有实数解,则输出 NO。

【输入】
输入的第一行包含两个正整数 T,M
,分别表示方程数和系数的绝对值上限。

接下来 T
行,每行包含三个整数 a,b,c

【输出】
输出 T
行,每行包含一个字符串,表示对应询问的答案,格式如题面所述。

每行输出的字符串中间不应包含任何空格。

【输入样例】
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
NO
1
-1
-1/2
12sqrt(3)
3/2+sqrt(5)/2
1+sqrt(2)/2
-7/2+3
sqrt(5)/2
【提示】
【数据范围】

对于所有数据有:1≤T≤5000
,1≤M≤103
,∣a∣,∣b∣,∣c∣≤M
,a≠0

测试点编号 M≤ 特殊性质 A 特殊性质 B 特殊性质 C
1 1 是 是 是
2 20 否 否 否
3 10^3 是 否 是
4 10^3 是 否 否
5 10^3 否 是 是
6 10^3 否 是 否
7,8 10^3 否 否 是
9,10 10^3 否 否 否
其中:

特殊性质 A:保证 b=0;

特殊性质 B:保证 c=0;

特殊性质 C:如果方程有解,那么方程的两个解都是整数。

没必说大模拟,撮合撮合还能用。

#include<bits/stdc++.h>
using namespace std;
int t, a, b, c, up;
int main() {
	cin >> t >> up;
	while (t--) {
		cin >> a >> b >> c;
		int derta = b * b - 4 * a * c;
		if (derta < 0) {
			cout << "NO\n";
			continue;
		}
		if ((int)sqrt(derta) * (int)sqrt(derta) == derta) {
			int z, m = 2 * a;
			if (a < 0) {
				z = -sqrt(derta) - b;
			} else {
				z = sqrt(derta) - b;
			}
			if (z > 0 && m < 0) {
				z = -z, m = -m;
			}
			if (z < 0 && m < 0) {
				z = -z, m = -m;
			}
			if (z % m == 0) {
				cout << z / m;
			} else {
				int g = __gcd(abs(m), abs(z));
				cout << z / g << "/" << m / g;
			}
		} else {
			int uz = -b, um = 2 * a, z = derta;
			if (uz != 0) {
				if (uz > 0 && um < 0) {
					uz = -uz, um = -um;
				}
				if (uz < 0 && um < 0) {
					uz = -uz, um = -um;
				}
				if (uz % um == 0) {
					cout << uz / um;
				} else {
					int g = __gcd(abs(um), abs(uz));
					cout << uz / g << "/" << um / g;
				}
				cout << "+";
			}
			int q2 = 1;
			for (int i = sqrt(derta); i >= 2; i--) {
				if (z % (i * i) == 0) {
					q2 = i;
					z /= i * i;
					break;
				}
			}
			int g = __gcd(abs(a) * 2, q2);
			a = abs(a) * 2 / g, q2 /= g;
			if (q2 == 1 && a == 1) {
				cout << "sqrt(" << z << ")";
			} else if (q2 == 1) {
				cout << "sqrt(" << z << ")/" << a;
			} else if (q2 % a == 0) {
				cout << q2 / a << "*sqrt(" << z << ")";
			} else {
				cout << q2 << "*sqrt(" << z << ")/" << a;
			}
		}
		cout << "\n";
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值