有一个长为 \(n\) 的序列 \(a\),有 \(m\) 次操作:
\(1 \leq n,m,a_i \leq 10^5\),时限 \(1.00\text{s}\),空限 \(512\text{MB}\)。
最初分块。
难度评分:\(8.5\)。
下面先假定 \(V\) 为值域。
先看查询区间第 \(k\) 大。
可以维护每个数在区间内出现了多少次。
这个可以用一个二维的 \(sum\) 数组维护:\(sum[i][j]\) 表示前 \(i\) 块中 \(j\) 出现的次数。
这样区间内一个数 \(x\) 出现的次数就是中间整块 \(sum\)的两个前缀和相减,再加上两边散块的出现次数,散块的出现个数可暴力。
然而这样时间复杂度为 \(\mathcal O(nV)\),无法通过。
考虑优化。
考虑再加上值域分块。
首先预处理 \(cnt1[i][j]\) 表示前 \(i\) 块中在第 \(j\) 块值域的数出现次数,\(cnt2[i][j]\) 表示前 \(i\) 块中 \(j\) 出现次数。
预处理 \(\mathcal{O}(n \sqrt{n})\)。
这样查询的时候,整块用 \(cnt1\) 和 \(cnt2\) 处理,散块用桶维护一下即可,单次时间复杂度为 \(\mathcal{O}(\sqrt{n})\)。
再看修改。
散块暴力即可。
再考虑并查集,记 \(id[x][i]\) 为第 \(x\) 块中第一个值为 \(i\) 的数的下标。
同时其逆也记下,记 \(rid[x][i]\) 表示第 \(x\) 块中下标 \(i\) 对应的值。
再用 \(pos_i\) 记录每一位的 \(id\)。(\(i\) 是序列上位置)
如果要还原序列,只需让 \(a_i=rid[bl_i][pos_i]\)。
再分情况讨论。
总时间和空间复杂度为 \(\mathcal O((n+m)\sqrt {n})\)。
总结:分块套分块,加套路并查集。
\(\text{5.97s / 202.38MB / 6.29KB C++98 O2}\)。
#include <cstdio> #include <cmath> #include <algorithm> namespace Fread { const int SIZE = 1 << 21; char buf[SIZE], *S, *T; inline char getchar() { if (S == T) { T = (S = buf) + fread(buf, 1, SIZE, stdin); if (S == T) return '\n'; } return *S++; } } namespace Fwrite { const int SIZE = 1 << 21; char buf[SIZE], *S = buf, *T = buf + SIZE; inline void flush() { fwrite(buf, 1, S - buf, stdout); S = buf; } inline void putchar(char c) { *S++ = c; if (S == T) flush(); } struct NTR { ~NTR() { flush(); } } ztr; } #ifdef ONLINE_JUDGE #define getchar Fread::getchar #define putchar Fwrite::putchar #endif inline int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); } return x * f; } inline void write(int x) { if (x < 0) { putchar('-'); x = -x; } if (x > 9) write(x / 10); putchar(x % 10 + '0'); } const int N = 1e5 + 7, SQ = 620; int n, m, a[N], len, bl[N], L[N], R[N], sum1[SQ], sum2[N]; int id[SQ][N], rid[SQ][N], pos[N]; int cnt1[SQ][SQ], cnt2[SQ][N]; inline void build(int x) { int tot = 0; for (int i = 1; i <= len; ++i) id[x][rid[x][i]] = 0; for (int i = L[x]; i <= R[x]; ++i) if (!id[x][a[i]]) { id[x][a[i]] = ++tot; rid[x][tot] = a[i]; } for (int i = L[x]; i <= R[x]; ++i) pos[i] = id[x][a[i]]; } inline void rest(int x) { for (int i = L[x]; i <= R[x]; ++i) a[i] = rid[x][pos[i]]; } inline void cg(int l, int r, int x, int y) { for (int i = l; i <= r; ++i) if (a[i] == x) { --cnt1[bl[l]][bl[x]], ++cnt1[bl[l]][bl[y]]; --cnt2[bl[l]][x], ++cnt2[bl[l]][y]; a[i] = y; } } inline void change(int i, int x, int y) { id[i][y] = id[i][x]; rid[i][id[i][x]] = y; id[i][x] = 0; } inline void modify(int l, int r, int x, int y) { if (x == y || cnt2[bl[r]][x] - cnt2[bl[l] - 1][x] == 0) return; for (int i = bl[n]; i >= bl[l]; --i) { cnt2[i][x] -= cnt2[i - 1][x]; cnt2[i][y] -= cnt2[i - 1][y]; cnt1[i][bl[x]] -= cnt1[i - 1][bl[x]]; cnt1[i][bl[y]] -= cnt1[i - 1][bl[y]]; } if (bl[l] == bl[r]) { rest(bl[l]), cg(l, r, x, y); build(bl[l]); for (int i = bl[l]; i <= bl[n]; ++i) { cnt2[i][x] += cnt2[i - 1][x]; cnt2[i][y] += cnt2[i - 1][y]; cnt1[i][bl[x]] += cnt1[i - 1][bl[x]]; cnt1[i][bl[y]] += cnt1[i - 1][bl[y]]; } return; } rest(bl[l]), rest(bl[r]); cg(l, R[bl[l]], x, y), cg(L[bl[r]], r, x, y); build(bl[l]), build(bl[r]); for (int i = bl[l] + 1; i < bl[r]; ++i) { if (!cnt2[i][x]) continue; if (cnt2[i][y]) { rest(i), cg(L[i], R[i], x, y); build(i); } else { cnt1[i][bl[y]] += cnt2[i][x]; cnt1[i][bl[x]] -= cnt2[i][x]; cnt2[i][y] = cnt2[i][x]; cnt2[i][x] = 0; change(i, x, y); } } for (int i = bl[l]; i <= bl[n]; ++i) { cnt2[i][x] += cnt2[i - 1][x]; cnt2[i][y] += cnt2[i - 1][y]; cnt1[i][bl[x]] += cnt1[i - 1][bl[x]]; cnt1[i][bl[y]] += cnt1[i - 1][bl[y]]; } } inline int kth(int l, int r, int k) { int sum = 0; if (bl[l] == bl[r]) { rest(bl[l]); for (int i = l; i <= r; ++i) sum2[i] = a[i]; std::nth_element(sum2 + l, sum2 + l + k - 1, sum2 + r + 1); int ans = sum2[l + k - 1]; for (int i = l; i <= r; ++i) sum2[i] = 0; return ans; } rest(bl[l]), rest(bl[r]); for (int i = l; i <= R[bl[l]]; ++i) { ++sum1[bl[a[i]]]; ++sum2[a[i]]; } for (int i = L[bl[r]]; i <= r; ++i) { ++sum1[bl[a[i]]]; ++sum2[a[i]]; } for (int i = 1; i <= (N - 1) / len + 1; ++i) if (sum + sum1[i] + cnt1[bl[r] - 1][i] - cnt1[bl[l]][i] >= k) { for (int j = (i - 1) * len + 1; j <= i * len; ++j) if (sum + sum2[j] + cnt2[bl[r] - 1][j] - cnt2[bl[l]][j] >= k) { for (int i = l; i <= R[bl[l]]; ++i) { --sum1[bl[a[i]]]; --sum2[a[i]]; } for (int i = L[bl[r]]; i <= r; ++i) { --sum1[bl[a[i]]]; --sum2[a[i]] = 0; } return j; } else sum += sum2[j] + cnt2[bl[r] - 1][j] - cnt2[bl[l]][j]; } else sum += sum1[i] + cnt1[bl[r] - 1][i] - cnt1[bl[l]][i]; } int main() { n = read(), m = read(); len = sqrt(n * 8 / 5); for (int i = 1; i < N; ++i) bl[i] = (i - 1) / len + 1; for (int i = 1; i <= n; ++i) a[i] = read(); for (int i = 1; i <= bl[n]; ++i) L[i] = (i - 1) * len + 1, R[i] = i * len; R[bl[n]] = n; for (int i = 1; i <= bl[n]; ++i) build(i); for (int x = 1; x <= bl[n]; ++x) { for (int i = 1; i <= bl[N - 1]; ++i) cnt1[x][i] = cnt1[x - 1][i]; for (int i = 1; i < N; ++i) cnt2[x][i] = cnt2[x - 1][i]; for (int i = L[x]; i <= R[x]; ++i) ++cnt1[x][bl[a[i]]], ++cnt2[x][a[i]]; } for (int i = 1, opt, l, r, x, y; i <= m; ++i) { opt = read(), l = read(), r = read(); if (opt == 1) { x = read(), y = read(); modify(l, r, x, y); } else { x = read(); write(kth(l, r, x)); putchar('\n'); } } return 0; }