题目链接
题目描述
给定 个一元三次方程,每个方程由三元组
描述,形式为
。你需要找到任意一个在区间
内的整数
,使得它不是这
个方程中任何一个的解。
数据范围:,
,
,
。
解题思路
本题的数据范围相较于旧版本有了极大的提升,这使得原先的解法失效,需要我们重新分析。
1. 解的存在性分析 (鸽巢原理)
- 根据代数基本定理,一个一元三次方程最多有 3 个整数解。
- 我们总共有
个方程,其中
。
- 因此,所有
个方程的整数解的总数,最多为
个。
- 我们需要寻找解的范围是整数区间
,这个区间内的整数总数为
个。
- 由于候选整数的数量(约
)大于所有可能的整数解的数量(最多
),根据鸽巢原理,必然存在一个在
内的整数,它不是任何方程的解。
2. 寻找解的策略
既然解一定存在,问题就在于如何高效地找到一个。
- 暴力枚举(不可行):如果我们对区间
内的每一个数
都去检查它是否为
个方程的解,时间复杂度将是
,会严重超时。
- 优化策略:我们已经证明了解是存在的。一个最直接的确定性算法是,我们从
开始依次向上测试,由于总根数不超过
,我们最多测试到
时,必然会遇到一个非根的数。但这个最坏情况的复杂度是
,当
时依然会超时。
- 竞赛常用策略:在这种情况下,通常的假设是测试数据不会极端地将所有小数都设置为解。因此,一个简单且大概率能通过的策略是,从小到大依次枚举
,并检查它是否为任何一个方程的解。只要找到第一个不是解的
,就立即输出。这个算法的复杂度是
,其中
是最终找到的答案。只要
是一个小常数(例如在几百以内),这个算法就能通过。
3. 注意事项
在计算 时,由于
和系数的范围很大,中间结果可能会超过 32 位甚至 64 位整数的范围。
- 对于 C++ 和 Java,需要使用 64 位整数 (
long long
,long
)。 - Python 的整数原生支持大数运算,没有溢出问题。
代码
#include <iostream>
#include <vector>
using namespace std;
struct Equation {
long long a, b, c;
};
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin >> n;
vector<Equation> equations(n);
for (int i = 0; i < n; ++i) {
cin >> equations[i].a >> equations[i].b >> equations[i].c;
}
// 从 0 开始向上枚举 x
for (long long x = 0; x <= 1000000; ++x) {
bool is_solution_for_any = false;
for (int i = 0; i < n; ++i) {
// 注意使用 long long 防止溢出
if (x * x * x + equations[i].a * x * x + equations[i].b * x + equations[i].c == 0) {
is_solution_for_any = true;
break;
}
}
if (!is_solution_for_any) {
cout << x << endl;
return 0;
}
}
return 0;
}
import java.util.Scanner;
public class Main {
static class Equation {
long a, b, c;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Equation[] equations = new Equation[n];
for (int i = 0; i < n; i++) {
equations[i] = new Equation();
equations[i].a = sc.nextLong();
equations[i].b = sc.nextLong();
equations[i].c = sc.nextLong();
}
// 从 0 开始向上枚举 x
for (long x = 0; x <= 1000000; x++) {
boolean isSolutionForAny = false;
for (int i = 0; i < n; i++) {
// 注意使用 long 防止溢出
if (x * x * x + equations[i].a * x * x + equations[i].b * x + equations[i].c == 0) {
isSolutionForAny = true;
break;
}
}
if (!isSolutionForAny) {
System.out.println(x);
return;
}
}
}
}
import sys
def solve():
n_str = sys.stdin.readline()
if not n_str: return
n = int(n_str)
equations = []
for _ in range(n):
a, b, c = map(int, sys.stdin.readline().split())
equations.append({'a': a, 'b': b, 'c': c})
# 从 0 开始向上枚举 x
for x in range(1000001):
is_solution_for_any = False
for eq in equations:
if x**3 + eq['a'] * x**2 + eq['b'] * x + eq['c'] == 0:
is_solution_for_any = True
break
if not is_solution_for_any:
print(x)
return
solve()
算法及复杂度
- 算法: 枚举、鸽巢原理
- 时间复杂度: 设找到的最小非负整数解为
。算法的复杂度为
。在最坏的情况下,
可能接近
,复杂度为
,但通常在竞赛中
会是一个小常数,使得该解法能够通过。
- 空间复杂度: 我们需要存储
个方程的系数。因此,空间复杂度为
。