CodeForces 1540D Inverse Inversions

洛谷传送门

CF 传送门

小清新题。

首先容易发现每个合法的 \(b\) 唯一对应一个排列,大概就是每个时刻排列元素的相对顺序,然后插入到相应的位置。

但是这样太麻烦了。发现题目只要求求单点的 \(p\) 值。这应该有更简单的方法。

考虑令 \(b_i \gets i - b_i\) 表示 \(p_i\) 在前缀 \([1, i]\) 的排名。那么我们时刻维护 \(p_i\),然后遍历 \(i + 1\)\(n\),若 \(b_i \le p\) 那么令 \(p \gets p + 1\)。这样我们得到了一个 \(O(nq)\) 的做法。

一种优化方向是分块。预处理出每个块每个数经过后会变成什么。设 \(x\) 经过后变成 \(f_x\)。容易发现 \(f\) 的值域为 \([1, len]\),也就是说它是一个 \(1 \sim len\) 的分段函数。那么求出每一段的断点就可以二分求出一个数经过这个块后会增加多少。

考虑如何对一个块预处理 \(f_x\)。相当于每次找到第一个 \(a_i + i \ge k\) 的位置 \(i\),然后对 \([i, n]\)\(1\)。容易树状数组维护,找到第一个位置可以树状数组上二分。

这样全部找到的位置就是分段函数的断点。

时间复杂度 \(O(n \sqrt{n} \log n)\)

code
// Problem: D. Inverse Inversions
// Contest: Codeforces - Codeforces Round 728 (Div. 1)
// URL: https://codeforces.com/problemset/problem/1540/D
// Memory Limit: 512 MB
// Time Limit: 5000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 100100;

int n, m, a[maxn], blo, bel[maxn], L[maxn], R[maxn], f[330][330];

struct BIT {
	int c[maxn];
	
	inline void update(int x, int d) {
		for (int i = x; i <= n; i += (i & (-i))) {
			c[i] += d;
		}
	}
	
	inline int find(int x) {
		int p = 0, s = 0;
		for (int i = 16; ~i; --i) {
			if (p + (1 << i) <= n && s + c[p + (1 << i)] < x) {
				s += c[p += (1 << i)];
			}
		}
		return p + 1;
	}
} B;

inline void build(int x) {
	for (int i = L[x]; i <= R[x]; ++i) {
		int p = B.find(a[i]);
		B.update(p, 1);
		f[x][i - L[x] + 1] = p;
	}
	sort(f[x] + 1, f[x] + R[x] - L[x] + 2);
	for (int i = 1; i <= R[x] - L[x] + 1; ++i) {
		B.update(f[x][i], -1);
	}
}

void solve() {
	scanf("%d", &n);
	blo = sqrt(n);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
		B.update(i, 1);
		a[i] = i - a[i];
		bel[i] = (i - 1) / blo + 1;
		if (!L[bel[i]]) {
			L[bel[i]] = i;
		}
		R[bel[i]] = i;
	}
	for (int i = 1; i <= bel[n]; ++i) {
		build(i);
	}
	scanf("%d", &m);
	while (m--) {
		int op, x, y;
		scanf("%d%d", &op, &x);
		if (op == 1) {
			scanf("%d", &y);
			a[x] = x - y;
			build(bel[x]);
		} else {
			int p = a[x];
			for (int i = x + 1; i <= R[bel[x]]; ++i) {
				p += (a[i] <= p);
			}
			for (int i = bel[x] + 1; i <= bel[n]; ++i) {
				int t = upper_bound(f[i] + 1, f[i] + R[i] - L[i] + 2, p) - f[i] - 1;
				p += t;
			}
			printf("%d\n", p);
		}
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

posted @ 2024-03-05 12:52  zltzlt  阅读(9)  评论(0编辑  收藏  举报