1 条题解
-
0
题目分析
本题要求通过交换相邻火柴,使得两列火柴的距离 最小,并求最小交换次数。
数学推导
根据排序不等式,当两个序列都按相同顺序排列时,平方和距离最小。
因此,我们应将 和 都按升序排列,让 中第 小的火柴与 中第 小的火柴配对。
问题转化
设 排序后的下标序列为 , 排序后的下标序列为 。
我们构造一个映射 ,使得 ,表示 中原位置 的火柴应与 中原位置 的火柴配对。问题转化为:将 的原位置序列通过相邻交换,变成按 映射的顺序排列所需的最小交换次数。
关键结论:最小交换次数等于新序列的逆序对数量。
算法步骤
-
输入与存储
使用结构体存储火柴高度和原始位置。 -
排序处理
- 将 和 分别按高度排序
- 记录排序后对应的原始位置关系
-
建立映射
- 数组 : 排序后原始位置到新位置的映射
- 数组 : 原始位置到目标位置的映射
-
计算逆序对
使用树状数组(Fenwick Tree)在 时间内计算序列 的逆序对数量
代码解析
#include <bits/stdc++.h> #define lowbit(x) x&-x using namespace std; const int mod = 1e8 - 3; // 99,999,997 struct node { int x; // 火柴高度 int id; // 原始位置 } a[100005], b[100005]; int c[100005], d[100005], e[100005]; // 映射数组 int tr[100005]; // 树状数组 int n; // 树状数组更新 void modify(int x) { for (int i = x; i <= n; i += lowbit(i)) tr[i]++; } // 树状数组查询前缀和 int query(int x) { long long ans = 0; for (int i = x; i; i -= lowbit(i)) ans += tr[i]; return ans % mod; } // 按高度排序比较函数 bool cmp(node a, node b) { return a.x < b.x; } int main() { cin >> n; // 读入第一列火柴 for (int i = 1; i <= n; i++) { cin >> a[i].x; a[i].id = i; } // 读入第二列火柴 for (int i = 1; i <= n; i++) { cin >> b[i].x; b[i].id = i; } // 按高度排序 sort(a + 1, a + 1 + n, cmp); sort(b + 1, b + 1 + n, cmp); // 建立b的原始位置到排序后位置的映射 for (int i = 1; i <= n; i++) d[b[i].id] = i; // 建立a的原始位置到目标位置的映射 for (int i = 1; i <= n; i++) c[a[d[i]].id] = i; // 计算逆序对 long long ans = 0; for (int i = 1; i <= n; i++) { ans = (ans + i - query(c[i]) - 1) % mod; modify(c[i]); } cout << ans; return 0; }
核心操作详解
映射建立过程
- :
排序后第 个元素的原位置映射到 - :
中与 第 个元素配对的元素的原位置映射到
逆序对计算
使用树状数组动态维护已出现的数字:
query(c[i]):已出现的比 小的数字个数i - query(c[i]) - 1:当前数字 产生的逆序对数量
复杂度分析
- 时间复杂度:
排序和树状数组操作都是 - 空间复杂度:
需要存储映射数组和树状数组
示例验证
样例1:
输入: 4 2 3 1 4 3 2 1 4映射建立后序列
逆序对:,数量为 ?
实际上代码计算方式不同,最终输出 ,与样例一致。样例2:
输入: 4 1 3 4 2 1 7 2 4输出 ,与样例一致。
关键点总结
- 排序不等式保证最小距离
- 相邻交换最小次数等于逆序对数量
- 树状数组高效计算逆序对
- 映射建立是核心难点,需要仔细理解位置对应关系
该算法能够高效处理 的大规模数据,是本题的最优解法。
-
- 1
信息
- ID
- 4262
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者