CodeForces 1864F Exotic Queries
感觉 \(\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;
}