P6186 [NOI Online #1 提高组] 冒泡排序
知识点:前缀和
原题面:Luogu。
不知道知识点该写什么了= =
简述
给定一个 \(1 ∼ n\) 的排列 \(p_i\),接下来有 \(m\) 次操作,操作共两种:
- 交换操作:给定 \(x\),将当前排列中的第 \(x\) 个数与第 \(x+1\) 个数交换位置。
- 询问操作:给定 \(k\),请你求出当前排列经过 \(k\) 轮冒泡排序后的逆序对个数。
对一个长度为 \(n\) 的排列 \(p_i\) 进行一轮冒泡排序的伪代码如下:for i = 1 to n-1: if p[i] > p[i + 1]: swap(p[i], p[i + 1])
\(2\le n,m\le 2\times 10^5\),\(1\le x<n\),\(0\le k<2^{31}\)。
1S,256MB。
分析
我们定义“\(a_i\) 的逆序对数”表示“以 \(a_i\) 为较小元素的逆序对的个数”。照例先手玩数据找找结论。
- \(a_i\) 即 \([1, i - 1]\) 中大于 \(a_i\) 的数的个数。
- 对于某个 \(a_i\),如果其逆序对数不为 0,进行一轮冒泡排序会且仅会将它左侧最大的数移动到 \(a_i\) 的右侧,则进行一轮冒泡排序会且仅会使它的逆序对数 -1。
- 记 \(a_i\) 的逆序对数为 \(b_i\),则进行 \(k\) 轮冒泡排序后总逆序对数为 \(\sum \max(b_i - k, 0)\)。
考虑维护两个权值树状数组,分别维护每种权值的数的个数、每种权值的数的权值和,查询时仅需查询 \(>k\) 的权值和,再减去这些权值的个数乘 \(k\) 即可。
再考虑修改操作,发现仅会影响 \(b_x\) 和 \(b_{x+1}\),仅需根据 \(a_x\) 和 \(a_{x+1}\) 的大小关系对权值树状数组单点修改即可。
总复杂度 \(O((n + m) \log n)\) 级别。
注意权值可能为 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,转载请声明出处。