P6186 [NOI Online #1 提高组] 冒泡排序

知识点:前缀和

原题面:Luogu

不知道知识点该写什么了= =

简述

给定一个 1n 的排列 pi,接下来有 m 次操作,操作共两种:

  1. 交换操作:给定 x,将当前排列中的第 x 个数与第 x+1 个数交换位置。
  2. 询问操作:给定 k,请你求出当前排列经过 k 轮冒泡排序后的逆序对个数。
    对一个长度为 n 的排列 pi 进行一轮冒泡排序的伪代码如下:
复制复制
for i = 1 to n-1:
if p[i] > p[i + 1]:
swap(p[i], p[i + 1])

2n,m2×1051x<n0k<231
1S,256MB。

分析

我们定义“ai 的逆序对数”表示“以 ai 为较小元素的逆序对的个数”。照例先手玩数据找找结论。

  • ai[1,i1] 中大于 ai 的数的个数。
  • 对于某个 ai,如果其逆序对数不为 0,进行一轮冒泡排序会且仅会将它左侧最大的数移动到 ai 的右侧,则进行一轮冒泡排序会且仅会使它的逆序对数 -1。
  • ai 的逆序对数为 bi,则进行 k 轮冒泡排序后总逆序对数为 max(bik,0)

考虑维护两个权值树状数组,分别维护每种权值的数的个数、每种权值的数的权值和,查询时仅需查询 >k 的权值和,再减去这些权值的个数乘 k 即可。

再考虑修改操作,发现仅会影响 bxbx+1,仅需根据 axax+1 的大小关系对权值树状数组单点修改即可。

总复杂度 O((n+m)logn) 级别。

注意权值可能为 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;
}
posted @   Luckyblock  阅读(51)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示