1 条题解
-
0
问题本质
这是一个动态维护冒泡排序扫描趟数的问题。对于长度为 的序列,有 次单点修改,每次修改后需要快速求出当前序列的冒泡排序扫描趟数。
关键结论
经过分析,冒泡排序的扫描趟数等于:

更具体地说,对于位置 ,定义:

那么冒泡排序的扫描趟数为 。
核心思路
1.问题转化
将问题转化为:维护每个位置 的 值,并在修改后快速求出全局最大值。
2.数据结构设计
使用权值线段树,以下标为权值(离散化后),维护:
-
cnt:区间内存在的元素个数
-
mx:区间内 的最大值(即原始位置减一)
-
res:区间内 的最大值
3.关键合并操作
线段树合并时:
void penup(int x) { tr[x].cnt = tr[ls(x)].cnt + tr[rs(x)].cnt; tr[x].mx = max(tr[ls(x)].mx, tr[rs(x)].mx); tr[x].res = max(tr[ls(x)].res, tr[rs(x)].res - tr[ls(x)].cnt); }这里 tr[rs(x)].res - tr[ls(x)].cnt 表示右子树中某个元素的 值,因为左子树的所有元素都小于右子树,所以左子树的元素个数就是右子树元素前面比它大的数的个数。
算法步骤
1.预处理阶段 离散化:将所有初始值和修改值一起离散化,注意要保留下标信息避免重复
for (int i = 1; i <= n; i++) p.push_back({a[i], i}); for (int i = 1; i <= T; i++) p.push_back({val[i], pos[i]}); stable_sort(p.begin(), p.end()); p.erase(unique(p.begin(), p.end()), p.end());2.建树初始化:
build(1, 1, p.size()); for (int i = 1; i <= n; i++) update(1, 1, p.size(), getid({a[i], i}), i - 1);查询处理 对于每次修改:
删除旧值:update(..., getid({a[pos[i]], pos[i]}), -INF)
更新数组:a[pos[i]] = val[i]
插入新值:update(..., getid({val[i], pos[i]}), pos[i] - 1)
输出答案:tr[1].res 就是全局的
复杂度分析
离散化:
建树:
每次修改:
总复杂度:,可以处理 的数据规模
总结
本题通过深入分析冒泡排序的性质,将扫描趟数转化为每个位置前面比它大的元素个数的函数,进而使用权值线段树动态维护这一信息。算法的核心在于:
问题转化:发现冒泡排序趟数与逆序对的分布关系
数据结构:设计能够维护 函数的线段树
合并策略:巧妙的区间合并方式,利用左右子树的偏序关系
离散化处理:处理大值域情况,保证复杂度
AC代码
#include <iostream> #include <cstdio> #include <algorithm> #include <stdlib.h> #include <cstring> #include <vector> #include <map> #define N 1000005 #define ls(x) (x << 1) #define rs(x) (x << 1 | 1) #define lson ls(x),l,mid #define rson rs(x),mid + 1,r using namespace std; int n, T, a[N], cnt, cnt2, pos[N], val[N]; const int INF = 1e9; vector<pair<int, int>> p; int getid(pair<int, int> x) { return lower_bound(p.begin(), p.end(), x) - p.begin() + 1; } struct seg { int cnt, mx, res; } tr[N << 2]; void penup(int x) { tr[x].cnt = tr[ls(x)].cnt + tr[rs(x)].cnt; tr[x].mx = max(tr[ls(x)].mx, tr[rs(x)].mx); tr[x].res = max(tr[ls(x)].res, tr[rs(x)].res - tr[ls(x)].cnt); } void build(int x, int l, int r) { tr[x] = {0, -INF, -INF}; if (l == r) return; int mid = l + r >> 1; build(lson), build(rson); } void update(int x, int l, int r, int pos, int val) { if (l == r) return tr[x] = {val != -INF, val, val}, void(); int mid = l + r >> 1; if (mid >= pos) update(lson, pos, val); else update(rson, pos, val); penup(x); } signed main() { // freopen("trunk.in","r",stdin),freopen("trunk.out","w",stdout); cin >> n >> T; for (int i = 1; i <= n; i ++) cin >> a[i]; for (int i = 1; i <= n; i ++) p.push_back({a[i], i}); for (int i = 1; i <= T; i ++) scanf("%d%d", &pos[i], &val[i]), pos[i] ++, p.push_back({val[i], pos[i]}); stable_sort(p.begin(), p.end()); p.erase(unique(p.begin(), p.end()), p.end()); build(1, 1, p.size()); // cout << p.size() << endl; for (int i = 1; i <= n; i ++) update(1, 1, p.size(), getid({a[i], i}), i - 1); // cout << getid({a[i],i}); // for (int i = 1;i <= p.size();i ++) cout << tr[i].res << endl; for (int i = 1; i <= T; i ++) { update(1, 1, p.size(), getid({a[pos[i]], pos[i]}), -INF); a[pos[i]] = val[i]; update(1, 1, p.size(), getid({val[i], pos[i]}), pos[i] - 1); printf("%d\n", tr[1].res); } return 0; } -
- 1
信息
- ID
- 4844
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者