1 条题解
-
0
这道题的核心在于两点。
1. 快速判定某一段子数组「是公差 > 1 的等差数列的子序列」
-
抽象为:一段序列去重、排序后,如果能嵌入一个等差公差 的完整等差数列,就符合要求。
-
关键等价条件:这段序列所有 相邻元素差值的最大公约数(GCD) ≥ 2。
-
设当前段为 (原序列顺序,不一定排好序),先按出现顺序计算相邻差值的绝对值序列
-
维护一个变量 ,定义为这些 的 GCD。只要最后 ,就说明存在一个公差为 (或其约数均 ≥ 2)的等差数列能包含所有 。
-
-
增量更新:每来一个新元素,只需计算它与前一个元素的差值 ,然后做一次 。如果得到的 就继续,否则就必须另起一段。
对于上述等价条件做一些补充证明:
-
若存在等差数列
使得去重排序后的 全部落在其中,那么必有
-
则所有相邻差值 都是 的倍数,因而 。
2. 注意去重——防止重复元素
-
题目要求「子序列」中不能出现重复值(否则无法严格算作注入到等差数列的不同位置)。
-
实现上只需给当前段维护一个哈希集合
state:-
每次要加入新元素前,先查询
state中是否已存在- 若存在 ⇒ 立即切段,重置
state并从该元素重新开始新一段 - 若不存在 ⇒ 插入
seen并继续做 GCD 更新
- 若存在 ⇒ 立即切段,重置
-
这样,扫描一次序列,同时维护当前段的差值 GCD 和 已出现元素集合,既能在线性时间内判定公差条件,又能及时发现重复而切分。
#include <bits/stdc++.h> // 引入 C++ 常用标准库头文件,包含向量、集合、算法、I/O 等 #define ll long long // 定义 ll 为 long long 的别名(本程序中未使用) #define mod 1000000007 // 定义常用取模数(本程序中未使用) #define eps 1e-7 // 定义浮点精度 eps(本程序中未使用) using namespace std; // 使用 std 命名空间,简化后续调用 const int N = 1e5 + 10; // 支持的最大机器人数量上限 + 余量 int n; // 机器人数量 n int a[N]; // 数组 a[1..n] 存储传送带上每台机器人的质量 /** * solve(): 计算最少的家族数量,使得将序列划分为若干段,每段满足: * - 所有质量构成的绝对差值子序列是公差 >1 的等差数列的子序列 * - 同一个家族的机器人是连续分段 */ void solve() { cin >> n; // 读取机器人数量 n for (int i = 1; i <= n; i++) cin >> a[i]; // 读取每台机器人的质量 a[i] int res = 1; // res 为所需家族数,至少 1 int d = -1; // d 存储当前段中公差(初始化为 -1 表示未设置) set<int> state; // state 用于记录当前段已经出现过的质量,防止重复 state.insert(a[1]); // 将首台机器人质量加入当前段 // 从第二台机器人开始遍历,贪心扩展当前段 for (int i = 2; i <= n; i++) { int t = abs(a[i] - a[i - 1]); // 计算相邻两台质量差的绝对值 t // 若 t < 2(表示差值 ≤1,不满足公差>1) // 或者本质量在当前段已出现(违反同段不重复) if (t < 2 || state.find(a[i]) != state.end()) { res++; // 需要开启新的家族(新段) d = -1; // 重置公差为未设置 state.clear(); // 清空当前段记录 state.insert(a[i]); // 新段首个元素为 a[i] continue; // 处理下一台机器人 } // 若当前段公差未设置,则用 t 初始化 if (d == -1) { d = t; // 设置 d 为当前差值 state.insert(a[i]); // 将 a[i] 加入当前段记录 continue; } // 若已有公差,则需要检查与当前差值 t 的 gcd // 若 gcd(t, d) == 1,则无法保持公差>1 的等差子序列性质,需要新段 if (__gcd(t, d) == 1) { res++; // 新家族(段) state.clear(); // 清空记录 d = -1; // 重置公差 } else { // 否则更新公差为 gcd(t, d),继续扩展当前段 d = __gcd(t, d); } state.insert(a[i]); // 将元素加入当前段记录 } cout << res; // 输出最少家族数 } int main() { int _ = 1; // scanf("%d", &_); while (_--) { solve(); } return 0; } -
- 1
信息
- ID
- 177
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 4
- 标签
- 递交数
- 31
- 已通过
- 6
- 上传者