1 条题解
-
0
思路与算法
先计算判别式 D=b²−4ac。D<0 输出 No answer!;D=0 输出 x1=x2;D>0 输出从小到大的两个实根。为了使数量级悬殊或根非常接近时仍能正确格式化,标程先把三个有限十进制数乘以共同的10的幂,得到等价整数系数 A、B、C。令 U=−sgn(A)·B、V=2|A|>0,实根即 (U±√D)/V。把根乘10⁵,用整数平方根得到其下取整,再把根与半整数的比较转为整数平方比较,最后输出固定5位小数。
正确性说明
统一乘共同非零尺度不改变方程的根。判别式的三分支正好对应无实根、一个重根、两个异根。V>0 后减号根不大于加号根。整数平方根 H 满足 H²≤D·10¹⁰<(H+1)²,可严格确定缩放根的整数部分;再比较根与 q+1/2,平方前单独判断符号,避免平方不等式改变方向。因此即使存在消减、下溢或很大输出,也不会依赖浮点近似来决定末位。
复杂度
设统一整数化后与缩放运算中最大整数有 D 个十进制数位。读入和尺度补零为 O(D),所用竖式乘法、长除法与逐两位开方均为 O(D²) 时间;只有常数次调用,因此总时间 O(D²),空间 O(D)。常规短浮点输入时可把这些数位量视作很小的常数。
易错点
a 可以为负,不能直接把公式的加号根认作较大根。D=0 应按精确判别式判断,不应用固定epsilon吞掉两个很近的根。注意两根即使格式化后相同,仍应走异根格式。结果为0时消去负零。数值、等号、分号之间没有空格。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; struct cpp_int { string s; bool neg; cpp_int(long long v = 0) : s(to_string(v)), neg(v < 0) { if (neg) s.erase(s.begin()); } void norm() { auto p = s.find_first_not_of('0'); s = p == string::npos ? "0" : s.substr(p); if (s == "0") neg = false; } static int cmpabs(const cpp_int &a, const cpp_int &b) { if (a.s.size() != b.s.size()) return a.s.size() < b.s.size() ? -1 : 1; return a.s == b.s ? 0 : a.s < b.s ? -1 : 1; } friend bool operator<(const cpp_int &a, const cpp_int &b) { if (a.neg != b.neg) return a.neg; int c = cmpabs(a, b); return a.neg ? c > 0 : c < 0; } friend bool operator==(const cpp_int &a, const cpp_int &b) { return a.neg == b.neg && a.s == b.s; } friend bool operator!=(const cpp_int &a, const cpp_int &b) { return !(a == b); } friend bool operator>(const cpp_int &a, const cpp_int &b) { return b < a; } friend bool operator<=(const cpp_int &a, const cpp_int &b) { return !(b < a); } friend bool operator>=(const cpp_int &a, const cpp_int &b) { return !(a < b); } cpp_int operator-() const { cpp_int r = *this; if (r.s != "0") r.neg = !r.neg; return r; } friend cpp_int operator+(const cpp_int &a, const cpp_int &b) { cpp_int r; r.s.clear(); int carry = 0; if (a.neg == b.neg) { int i = (int)a.s.size() - 1, j = (int)b.s.size() - 1; while (i >= 0 || j >= 0 || carry) { int v = carry + (i >= 0 ? a.s[i--] - '0' : 0) + (j >= 0 ? b.s[j--] - '0' : 0); r.s += char('0' + v % 10); carry = v / 10; } r.neg = a.neg; } else { const cpp_int *x = &a, *y = &b; if (cmpabs(a, b) < 0) swap(x, y); int i = (int)x->s.size() - 1, j = (int)y->s.size() - 1; while (i >= 0) { int v = x->s[i--] - '0' - carry - (j >= 0 ? y->s[j--] - '0' : 0); carry = v < 0; if (carry) v += 10; r.s += char('0' + v); } r.neg = x->neg; } reverse(r.s.begin(), r.s.end()); r.norm(); return r; } friend cpp_int operator-(const cpp_int &a, const cpp_int &b) { return a + (-b); } friend cpp_int operator*(const cpp_int &a, int b) { cpp_int r; r.s.clear(); long long carry = 0; long long z = b; if (z < 0) z = -z; for (int i = (int)a.s.size() - 1; i >= 0; i--) { carry += (a.s[i] - '0') * z; r.s += char('0' + carry % 10); carry /= 10; } while (carry) { r.s += char('0' + carry % 10); carry /= 10; } reverse(r.s.begin(), r.s.end()); r.neg = a.neg ^ (b < 0); r.norm(); return r; } friend cpp_int operator/(const cpp_int &a, int b) { cpp_int r; r.s.clear(); long long rem = 0, z = b; if (z < 0) z = -z; for (char c : a.s) { rem = rem * 10 + c - '0'; r.s += char('0' + rem / z); rem %= z; } r.neg = a.neg ^ (b < 0); r.norm(); return r; } cpp_int &operator+=(const cpp_int &b) { *this = *this + b; return *this; } cpp_int &operator-=(const cpp_int &b) { *this = *this - b; return *this; } cpp_int &operator*=(int b) { *this = *this * b; return *this; } friend istream &operator>>(istream &in, cpp_int &a) { string v; if (in >> v) { a.neg = v[0] == '-'; if (v[0] == '-' || v[0] == '+') v.erase(v.begin()); a.s = v; a.norm(); } return in; } friend ostream &operator<<(ostream &out, const cpp_int &a) { if (a.neg) out << '-'; return out << a.s; } }; cpp_int fromString(string s) { cpp_int a; a.neg = !s.empty() && s[0] == '-'; if (!s.empty() && (s[0] == '-' || s[0] == '+')) s.erase(s.begin()); a.s = s; a.norm(); return a; } cpp_int absBig(cpp_int a) { a.neg = false; return a; } cpp_int pow10(size_t n) { cpp_int a; a.s = "1" + string(n, '0'); return a; } cpp_int operator*(const cpp_int &a, const cpp_int &b) { vector<long long> d(a.s.size() + b.s.size()); for (int i = (int)a.s.size() - 1; i >= 0; --i) for (int j = (int)b.s.size() - 1; j >= 0; --j) d[i + j + 1] += (a.s[i] - '0') * (b.s[j] - '0'); for (int i = (int)d.size() - 1; i > 0; --i) { d[i - 1] += d[i] / 10; d[i] %= 10; } cpp_int c; c.s.clear(); for (auto x : d) c.s += char('0' + x); c.neg = a.neg != b.neg; c.norm(); return c; } pair<cpp_int, cpp_int> divmod(cpp_int a, cpp_int b) { bool sign = a.neg != b.neg, negrem = a.neg; a.neg = b.neg = false; cpp_int q, rem; q.s.clear(); cpp_int mul[10]; for (int i = 0; i < 10; ++i) mul[i] = b * i; for (char z : a.s) { rem = rem * 10 + (z - '0'); int lo = 0, hi = 9; while (lo < hi) { int mi = (lo + hi + 1) / 2; if (mul[mi] <= rem) lo = mi; else hi = mi - 1; } q.s += char('0' + lo); rem -= mul[lo]; } q.neg = sign; q.norm(); rem.neg = negrem; rem.norm(); return {q, rem}; } cpp_int floorDiv(cpp_int a, const cpp_int &b) { auto z = divmod(a, b); if (a.neg && z.second != 0) z.first -= 1; return z.first; } cpp_int rootBig(const cpp_int &a) { cpp_int root, rem; string s = a.s; if (s.size() % 2) s = "0" + s; for (size_t i = 0; i < s.size(); i += 2) { rem = rem * 100 + (s[i] - '0') * 10 + s[i + 1] - '0'; int x = 9; while ((root * 20 + x) * x > rem) --x; rem -= (root * 20 + x) * x; root = root * 10 + x; } return root; } string fixedBig(cpp_int rounded, int digits) { bool neg = rounded.neg; string s = rounded.s; while (s.size() <= (size_t)digits) s = "0" + s; if (digits) s.insert(s.size() - digits, "."); return (neg ? "-" : "") + s; } cpp_int roundedRational(cpp_int a, cpp_int b) { bool neg = a.neg != b.neg; a.neg = b.neg = false; auto q = divmod(a, b); if (q.second * 2 >= b) q.first += 1; q.first.neg = neg && q.first != 0; return q.first; } pair<cpp_int, long long> decimal(string s) { size_t ep = s.find_first_of("eE"); long long e = 0; if (ep != string::npos) { e = stoll(s.substr(ep + 1)); s = s.substr(0, ep); } size_t dp = s.find('.'); if (dp != string::npos) { e -= (long long)(s.size() - dp - 1); s.erase(dp, 1); } return {fromString(s), e}; } cpp_int rootRounded(cpp_int U, cpp_int V, cpp_int D, int branch) { cpp_int W = U * 100000, Z = D * pow10(10), H = rootBig(Z); bool square = H * H == Z; cpp_int num = W + H * branch; cpp_int q = floorDiv(num, V); if (branch < 0 && !square && divmod(num, V).second == 0) q -= 1; cpp_int T = V * (q * 2 + 1) - W * 2; int cmp; if (branch > 0) { if (T < 0) cmp = 1; else { cpp_int x = Z * 4, y = T * T; cmp = x == y ? 0 : (x > y ? 1 : -1); } } else { if (T > 0) cmp = -1; else { cpp_int x = T * T, y = Z * 4; cmp = x == y ? 0 : (x > y ? 1 : -1); } } if (cmp > 0 || (cmp == 0 && q >= 0)) q += 1; return q; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); string s[3]; for (auto &x : s) cin >> x; auto a = decimal(s[0]), b = decimal(s[1]), c = decimal(s[2]); long long e = min(a.second, min(b.second, c.second)); cpp_int A = a.first * pow10(a.second - e), B = b.first * pow10(b.second - e), C = c.first * pow10(c.second - e); cpp_int D = B * B - A * C * 4; if (D < 0) { cout << "No answer!\n"; return 0; } cpp_int U = A.neg ? B : -B, V = absBig(A) * 2; if (D == 0) cout << "x1=x2=" << fixedBig(roundedRational(U * 100000, V), 5) << '\n'; else cout << "x1=" << fixedBig(rootRounded(U, V, D, -1), 5) << ";x2=" << fixedBig(rootRounded(U, V, D, 1), 5) << '\n'; }
- 1