CodeForces 1864F Exotic Queries

发布时间 2023-08-29 08:06:15作者: zltzlt

洛谷传送门

CF 传送门

感觉 \(\text{F < E}\)

考虑 \(l = 1, r = n\) 怎么做。

考虑分治。设 \(\text{solve}(l, r)\) 为只考虑下标\([l, r]\) 中的数的最小操作次数。

\(x = \min\limits_{i = l}^r a_i\)。容易发现给这个区间减去 \(x\) 最优,于是 \(\forall i \in [l, r], a_i \gets a_i - x\)。设最小值出现的位置为 \(p_1, p_2, \ldots, p_k\),那么对 \(\text{solve}(l, p_1 - 1), \text{solve}(p_1 + 1, p_2 - 1), \ldots, \text{solve}(p_{k - 1} + 1, p_k - 1), \text{solve}(p_k + 1, r)\) 求和,再 \(+ 1\) 就是 \(\text{solve}(l, r)\) 的答案。

考虑 \(l = 1\) 怎么做。先离线所有询问,考虑维护答案,从小到大加入数,并计算出 \(r - 1 \to r\) 时,答案的变化量。

设当前加入所有 \(a_j = i\) 的数,若有这样的数先令答案 \(+ 1\),因为至少要操作 \(1\) 次;设这样的数的位置为 \(j_1, j_2, \ldots, j_k\),我们在 \(j_{t - 1}, j_t\) 处计算答案的变化量:

  • \([j_{t - 1}, j_t]\) 中没有之前已经加入的数,那么答案不会变化(因为操作 \(j_{t - 1}\) 可以顺带把 \(j_t\) 操作);
  • \([j_{t - 1}, j_t]\) 中有之前已经加入的数,那么答案 \(+ 1\)

考虑一般情况,我们发现和 \(l = 1\) 不同之处在于若 \([j_{t - 1}, j_t]\) 中有之前已经加入的数,答案也不一定 \(+ 1\)。具体地,设 \(x\)\([j_{t - 1}, j_t]\) 中之前已经加入的数的最大值,那么当 \(l > x\) 时答案就不用 \(+ 1\)

于是我们用树状数组维护每个 \(x\),回答 \([l, r]\) 时就知道有多少个 \(x\) 满足 \(l > x\)

再使用一棵线段树维护数的加入情况,复杂度就是 \(O((n + q) \log n)\)

code
// Problem: F. Exotic Queries
// Contest: Codeforces - Harbour.Space Scholarship Contest 2023-2024 (Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/1864/problem/F
// Memory Limit: 1024 MB
// Time Limit: 4000 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<int, int> pii;

const int maxn = 1000100;
const int logn = 22;

int n, a[maxn], m, f[maxn][logn], ans[maxn], g[maxn];
vector<pii> vc[maxn];
vector<int> app[maxn];

inline int qmax(int l, int r) {
	int k = __lg(r - l + 1);
	return max(f[l][k], f[r - (1 << k) + 1][k]);
}

namespace 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 query(int x) {
		int res = 0;
		for (int i = x; i; i -= (i & (-i))) {
			res += c[i];
		}
		return res;
	}
}

namespace SGT {
	int tree[maxn << 2];
	
	inline void init() {
		mems(tree, -0x3f);
	}
	
	inline void pushup(int x) {
		tree[x] = max(tree[x << 1], tree[x << 1 | 1]);
	}
	
	void update(int rt, int l, int r, int x, int y) {
		if (l == r) {
			tree[rt] = y;
			return;
		}
		int mid = (l + r) >> 1;
		(x <= mid) ? update(rt << 1, l, mid, x, y) : update(rt << 1 | 1, mid + 1, r, x, y);
		pushup(rt);
	}
	
	int query(int rt, int l, int r, int ql, int qr) {
		if (ql > qr) {
			return -2e9;
		}
		if (ql <= l && r <= qr) {
			return tree[rt];
		}
		int mid = (l + r) >> 1, res = -2e9;
		if (ql <= mid) {
			res = max(res, query(rt << 1, l, mid, ql, qr));
		}
		if (qr > mid) {
			res = max(res, query(rt << 1 | 1, mid + 1, r, ql, qr));
		}
		return res;
	}
}

void solve() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
		app[a[i]].pb(i);
	}
	for (int i = 1, l, r; i <= m; ++i) {
		scanf("%d%d", &l, &r);
		vc[r].pb(l, i);
	}
	SGT::init();
	int res = 0;
	for (int i = 1; i <= n; ++i) {
		g[i] = g[i - 1];
		if (app[i].size()) {
			++res;
			++g[i];
		}
		for (int j = 1; j < (int)app[i].size(); ++j) {
			int t = SGT::query(1, 1, n, app[i][j - 1] + 1, app[i][j] - 1);
			if (t > -1e9) {
				++res;
				BIT::update(t + 1, -1);
			}
		}
		for (int x : app[i]) {
			SGT::update(1, 1, n, x, i);
		}
		for (pii p : vc[i]) {
			int l = p.fst, id = p.scd;
			ans[id] = res + BIT::query(l) - g[l - 1];
		}
	}
	for (int i = 1; i <= m; ++i) {
		printf("%d\n", ans[i]);
	}
}

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