P6186 [NOI Online #1 提高组] 冒泡排序
知识点:前缀和
原题面:Luogu。
不知道知识点该写什么了= =
简述
给定一个 的排列 ,接下来有 次操作,操作共两种:
- 交换操作:给定 ,将当前排列中的第 个数与第 个数交换位置。
- 询问操作:给定 ,请你求出当前排列经过 轮冒泡排序后的逆序对个数。
对一个长度为 的排列 进行一轮冒泡排序的伪代码如下:复制复制
for i = 1 to n-1: if p[i] > p[i + 1]: swap(p[i], p[i + 1]) ,,。
1S,256MB。
分析
我们定义“ 的逆序对数”表示“以 为较小元素的逆序对的个数”。照例先手玩数据找找结论。
- 即 中大于 的数的个数。
- 对于某个 ,如果其逆序对数不为 0,进行一轮冒泡排序会且仅会将它左侧最大的数移动到 的右侧,则进行一轮冒泡排序会且仅会使它的逆序对数 -1。
- 记 的逆序对数为 ,则进行 轮冒泡排序后总逆序对数为 。
考虑维护两个权值树状数组,分别维护每种权值的数的个数、每种权值的数的权值和,查询时仅需查询 的权值和,再减去这些权值的个数乘 即可。
再考虑修改操作,发现仅会影响 和 ,仅需根据 和 的大小关系对权值树状数组单点修改即可。
总复杂度 级别。
注意权值可能为 0,需要为树状数组的下标添加增量。
代码
//By:Luckyblock /* */ #include <cstdio> #include <cctype> #include <algorithm> #define LL long long const int kN = 2e5 + 10; //============================================================= int n, m, a[kN], b[kN]; LL ans = 0; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0'; return f * w; } namespace Bit1 { #define lowbit(x) (x&-x) const int lim = 1e6; const int kM = lim + 10; LL t[kM]; void Insert(int pos_, int val_) { for (int i = pos_; i <= lim; i += lowbit(i)) { t[i] += val_; } } LL sum(int pos_) { LL ret = 0; for (int i = pos_; i; i -= lowbit(i)) { ret += t[i]; } return ret; } LL Query(int pos_) { return sum(lim) - sum(pos_); } } namespace Bit2 { #define lowbit(x) (x&-x) const int lim = 1e6; const int kM = lim + 10; LL t[kM], cnt[kM]; void Insert(int pos_, int val_, int c_) { ++ pos_; for (int i = pos_; i <= lim + 1; i += lowbit(i)) { t[i] += val_; cnt[i] += c_; } } LL sumt(int pos_) { ++ pos_; LL ret = 0; for (int i = pos_; i; i -= lowbit(i)) ret += t[i]; return ret; } LL sumcnt(int pos_) { ++ pos_; LL ret = 0; for (int i = pos_; i; i -= lowbit(i)) ret += cnt[i]; return ret; } LL Query(int k_) { k_ = std::min(k_, n - 1); LL s1 = sumt(lim) - sumt(k_); LL s2 = sumcnt(lim) - sumcnt(k_); return s1 - s2 * k_; } } //============================================================= int main() { // freopen("1.txt", "r", stdin); n = read(), m = read(); for (int i = 1; i <= n; ++ i) { a[i] = read(); b[i] = Bit1::Query(a[i]); Bit1::Insert(a[i], 1); Bit2::Insert(b[i], b[i], 1); } while (m --) { int opt = read(), x = read(); if (opt == 1) { int ax = a[x], ax1 = a[x + 1]; int bx = b[x], bx1 = b[x + 1]; std::swap(a[x], a[x + 1]), std::swap(b[x], b[x + 1]); Bit2::Insert(bx, -bx, -1), Bit2::Insert(bx1, -bx1, -1); if (ax < ax1) { ++ b[x + 1]; Bit2::Insert(bx1, bx1, 1); Bit2::Insert(bx + 1, bx + 1, 1); } else { -- b[x]; Bit2::Insert(bx1 - 1, bx1 - 1, 1); Bit2::Insert(bx, bx, 1); } } else { printf("%lld\n", Bit2::Query(x)); } } return 0; }
作者@Luckyblock,转载请声明出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!