题目概述
给定一个4次整数系数多项式,要求将其分解为若干个质多项式(不可再分解的整数系数多项式)的乘积,并按照特定格式输出。
输入格式:多组测试数据,每组包含5个整数,表示4次多项式的系数(从4次项到常数项)
输出格式:每行输出一个质多项式的系数,按特定规则排序
算法思路
核心思想
本解法采用深度优先搜索(DFS\texttt{DFS}DFS)结合有理根定理的方法,系统地寻找多项式的整数系数因子。
关键步骤
1. 有理根定理的应用
对于整数系数多项式,任何有理根必为常数项因子与首项系数因子的比值。我们利用这一性质生成所有可能的线性因子候选。
2. 多项式除法
实现多项式长除法算法,检查候选因子是否能整除原多项式。
3. 递归分解
- 优先尝试线性因子分解
- 对于4次多项式,还尝试二次因子分解
- 无法分解时,当前多项式即为质多项式
4. 系数归一化
确保输出满足题目要求:首项系数为正,系数互质
代码详解
多项式除法函数
bool divisible(vector<int> a, vector<int> b, vector<int>& q) {
q.clear();
for (int i = 0; i + b.size() <= a.size(); i++) {
if (a[i] % b[0]) return false; // 检查可除性
int r = a[i] / b[0]; q.push_back(r); // 计算商的一项
// 执行减法步骤
for (int j = 0; j < b.size(); j++) a[i + j] -= r * b[j];
}
// 检查余数是否为零
for (int x : a) if (x) return false;
return true;
}
因子生成函数
void getFactors(int n, vector<int>& f) {
f.clear();
for (int i = 1; i * i <= n; i++) {
if (n % i == 0) {
f.push_back(i);
f.push_back(n / i);
}
}
}
核心分解函数
bool dfs(int d, vector<int> in, vector<vector<int>>& out) {
if (d == 1) { out.push_back(in); return true; } // 基本情况
vector<int> p, q, r;
getFactors(in[0], p); // 首项系数因子
getFactors(abs(in.back()), q); // 常数项因子
// 生成所有可能的线性因子候选
int qsize = q.size();
for (int i = 0; i < qsize; i++) q.push_back(-q[i]);
if (in.back() == 0) q.push_back(0);
// 尝试线性因子分解
for (int a1 : p) for (int c1 : q) {
vector<int> t = {a1, c1};
if (divisible(in, t, r)) {
out.push_back(t);
return dfs(d - 1, r, out); // 递归分解商式
}
}
// 二次因子分解(仅对4次多项式)
if (d == 4) {
// 尝试分解为两个二次多项式的乘积
// 详细实现见完整代码
}
out.push_back(in); return false;
}
算法复杂度分析
- 时间复杂度:最坏情况下需要检查所有可能的因子组合,但由于多项式次数固定为4,实际运行效率很高
- 空间复杂度:主要消耗在递归调用栈和存储分解结果,由于深度有限,空间需求不大
样例分析
样例1
输入:2 7 -1 -6 -2
分解过程:
- 找到线性因子
(x - 1),执行除法得到2x³ + 9x² + 8x + 2 - 找到线性因子
(2x + 1),执行除法得到x² + 4x + 2 x² + 4x + 2无法再分解
输出:
1 -1
2 1
1 4 2
样例2
输入:2 0 0 0 0
分解:2x⁴ = 2 × x × x × x × x
输出:
1 0
1 0
1 0
2 0
实现要点
-
首项系数处理:如果输入首项系数为负,先整体取反,最后再调整回来
-
系数归一化:每个因子的系数要除以它们的最大公约数
-
输出排序:
- 按多项式次数升序排列
- 同次数按字典序排列
- 整体符号调整到最后一个多项式
-
边界情况:正确处理常数项为零的情况
总结
本题的关键在于合理应用有理根定理和系统性的搜索策略。通过优先尝试简单因子(线性因子),再考虑复杂分解(二次因子),算法能够在可接受的时间内找到正确的分解方案。系数归一化和输出排序的处理确保了结果符合题目要求。
参考代码
// Polynomial Factorization
// UVa ID: 463
// Verdict: Accepted
// Submission Date: 2025-10-07
// UVa Run Time: 0.080s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
/**
* 多项式除法:判断多项式a是否能被多项式b整除
* @param a 被除多项式(系数向量,高次在前)
* @param b 除多项式(系数向量,高次在前)
* @param q 商多项式(输出参数)
* @return 如果可整除返回true,否则返回false
*/
bool divisible(vector<int> a, vector<int> b, vector<int>& q) {
q.clear();
// 模拟多项式长除法过程
for (int i = 0; i + b.size() <= a.size(); i++) {
// 检查当前最高次项是否能整除
if (a[i] % b[0]) return false;
// 计算商的一项
int r = a[i] / b[0]; q.push_back(r);
// 从被除数中减去商乘以除数的结果
for (int j = 0; j < b.size(); j++) a[i + j] -= r * b[j];
}
// 检查余数是否为零(完全整除)
for (int x : a) if (x) return false;
return true;
}
/**
* 获取一个整数的所有因子(正因子)
* @param n 输入整数
* @param f 因子列表(输出参数)
*/
void getFactors(int n, vector<int>& f) {
f.clear();
// 遍历1到sqrt(n)寻找因子
for (int i = 1; i * i <= n; i++) {
if (n % i == 0) {
f.push_back(i); // 小因子
f.push_back(n / i); // 对应的配对因子
}
}
}
// 比较函数:按绝对值升序排序
bool cmpValue(int a, int b) { return abs(a) < abs(b); }
// 比较函数:多项式排序(先按次数升序,次数相同按字典序)
bool cmpPoly(vector<int> a, vector<int> b) {
return a.size() != b.size() ? a.size() < b.size() : a < b;
}
/**
* 深度优先搜索分解多项式
* @param d 当前多项式的次数
* @param in 当前要分解的多项式系数
* @param out 分解结果(输出参数)
* @return 是否成功分解
*/
bool dfs(int d, vector<int> in, vector<vector<int>>& out) {
// 基本情况:次数为1,无法再分解
if (d == 1) { out.push_back(in); return true; }
vector<int> p, q, r;
// 获取首项系数和常数项的所有因子
getFactors(in[0], p); // 首项系数的因子
getFactors(abs(in.back()), q); // 常数项的因子(取绝对值)
// 为常数项因子添加负值版本(因为根可能是负数)
int qsize = q.size();
for (int i = 0; i < qsize; i++) q.push_back(-q[i]);
if (in.back() == 0) q.push_back(0); // 处理常数项为零的情况
// 按绝对值排序,优先尝试绝对值小的因子(提高效率)
sort(p.begin(), p.end(), cmpValue);
sort(q.begin(), q.end(), cmpValue);
// 尝试所有可能的线性因子 (a1*x + c1)
for (int a1 : p) for (int c1 : q) {
vector<int> t = {a1, c1}; // 构造线性因子
if (divisible(in, t, r)) { // 检查是否能整除
out.push_back(t); // 将因子加入结果
return dfs(d - 1, r, out); // 递归分解商式
}
}
// 如果次数较低(2或3)且找不到线性因子,直接返回原多项式
if (d <= 3) { out.push_back(in); return true; }
// 对于4次多项式,尝试二次因子分解 (a1*x² + b1*x + c1)(a2*x² + b2*x + c2)
for (int a1 : p) for (int c1 : q) {
int a2 = in[0] / a1, c2 = in.back() / c1; // 计算配对系数
// 根据多项式乘法展开,建立方程组:
// 原多项式: a0*x⁴ + a1*x³ + a2*x² + a3*x + a4
// 分解形式: (a1*x² + b1*x + c1)(a2*x² + b2*x + c2)
// 展开后系数对应关系:
// x⁴: a1*a2 = a0
// x³: a1*b2 + a2*b1 = a1
// x²: a1*c2 + b1*b2 + a2*c1 = a2
// x¹: b1*c2 + b2*c1 = a3
// x⁰: c1*c2 = a4
// 从x³和x¹的方程中解出b1和b2
// 设方程为:a2*b1 + a1*b2 = a1 (x³系数)
// c2*b1 + c1*b2 = a3 (x¹系数)
// 这可以看作关于b1, b2的线性方程组
int aa = -a2, bb = in[1]; // 系数矩阵参数
int cc = -(a1 * in[2] - a1 * a1 * c2 - a2 * c1 * a1); // 计算判别式参数
// 计算判别式
int delta = bb * bb - 4 * aa * cc;
if (delta < 0) continue; // 无实数解
int hh = sqrt(delta + 0.5); // 开方(加0.5用于四舍五入)
if (hh * hh != delta) continue; // 检查是否为完全平方数
// 尝试两个可能的根(正负两个解)
for (int sign = -1; sign <= 1; sign += 2) {
// 检查解是否为整数
if ((sign * hh - bb) % (2 * aa)) continue;
int b1 = (sign * hh - bb) / (2 * aa); // 计算b1
// 从x³方程计算b2,检查是否为整数
if ((in[1] - b1 * a2) % a1) continue;
int b2 = (in[1] - b1 * a2) / a1;
// 验证x¹系数方程
if (b1 * c2 + b2 * c1 != in[3]) continue;
// 找到有效的二次因子分解
out.push_back({a1, b1, c1});
out.push_back({a2, b2, c2});
return true;
}
}
// 无法分解,返回原多项式
out.push_back(in);
return false;
}
int main() {
cin.tie(0); ios::sync_with_stdio(false); // 加速IO
int a, b, c, d, e;
while (cin >> a >> b >> c >> d >> e) {
// 读取5个系数(4次多项式)
vector<int> in = {a, b, c, d, e};
// 如果首项系数为负,整体取反(保证首项系数为正)
bool reversed = in[0] < 0;
if (reversed) for (int& x : in) x *= -1;
// 分解多项式
vector<vector<int>> out;
dfs(4, in, out);
// 计算所有因子的最大公约数,用于归一化系数
int g = 1;
for (auto& poly : out) {
int tmpg = 0;
// 计算当前多项式的系数的GCD
for (int x : poly) if (x) tmpg = tmpg ? __gcd(tmpg, abs(x)) : abs(x);
if (!tmpg) tmpg = 1; // 处理全零情况
g *= tmpg;
// 将当前多项式系数除以GCD进行归一化
for (int& x : poly) x /= tmpg;
}
// 如果之前取反过,现在将整体符号调整回来
if (reversed) g = -g;
// 对因子排序:先按次数升序,次数相同按字典序
sort(out.begin(), out.end(), cmpPoly);
// 将整体符号乘到最后一个因子上
for (int& x : out.back()) x *= g;
// 输出结果
for (auto& poly : out) {
for (auto it = poly.begin(); it != poly.end(); ++it) {
cout << *it;
if (next(it) != poly.end()) cout << " "; // 空格分隔,最后无空格
}
cout << "\n";
}
cout << "\n"; // 每个测试用例后输出空行
}
return 0;
}

9569

被折叠的 条评论
为什么被折叠?



