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

知识点:前缀和

原题面:Luogu

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

简述

给定一个 \(1 ∼ n\) 的排列 \(p_i\),接下来有 \(m\) 次操作,操作共两种:

  1. 交换操作:给定 \(x\),将当前排列中的第 \(x\) 个数与第 \(x+1\) 个数交换位置。
  2. 询问操作:给定 \(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;
}
posted @ 2023-01-13 16:48  Luckyblock  阅读(47)  评论(0编辑  收藏  举报