meaning of the title
Given a sequence with a length of \ (n \) and \ (m \) operations, each operation can:
-
Change all values \ (x \) in \ ([l, r] \) to the value \ (y \)
-
Query the value smaller than \ (k \) in \ ([l, r] \)
The same value is counted multiple times.
\(1 \leq n, m, a_i \leq 10^5\)
thinking
Initially divided into blocks.
Block + value range block + joint search set.
Obviously, the idea is to find the small value of static \ (k \) by binary tree array, but the complexity is not right.
Consider using range blocks instead of bisection.
Divide the value range \ ([1, 10 ^ 5] \) into \ (\ sqrt{10^5} \) blocks.
Let \ (sum1 {i, j} \) represent the number of values in the \ (j \) value range block in the first \ (i \) block, and \ (sum2 {i, j} \) represent the number of values \ (j \) in the first \ (i \) block. These two arrays can be preprocessed with \ (\ mathcal{O}(n \sqrt{n}) \).
When inquiring about \ ([l, r] \), process additionally \ (cnt1 {j} \) represents the number of values in the \ (j \) value range block in the hash block, \ (CNT2 {j} \) represents the number of values \ (j \) in the hash block. When the block length is \ (\ sqrt{n} \), the complexity of processing these two arrays is \ (\ mathcal{O}(\sqrt{n}) \)
Suppose the inquiry is smaller than \ (k \).
First, determine the value range block where the \ (k \) smallest value is located. The specific implementation can make \ (sum \) initially \ (0 \), and when enumerating the range block \ (i \), the order \ (sum \) continuously accumulates the number of values in the \ (i \) range block in \ ([l, r] \) (calculated with the array \ (\ mathcal{O}(1) \) processed above). When \ (sum \geq k \), it indicates that the value is in the \ (i \) range block. The complexity of enumeration range block is \ (\ mathcal{O}(\sqrt{n}) \)
Then enumerate the values in the value range block, and use the method similar to the above to find the small value of \ (k \). Complexity is also \ (\ mathcal{O}(\sqrt{n}) \)
be similar to P4117 [Ynoi2018] colorful world , maintenance \ (rt_{I, j}, val_{I, j}, pos_, I \)\ (RT {I, j} = k \) indicates that \ (K \) is the value in the \ (I \) th block \ (j \) and the root of the query set is \ (K \), \ (Val {I, j} = k \) indicates that the value corresponding to the root in the \ (I \) th block \ (j \) is \ (K \), \ (pos_i = RT {I, a_i} \)
You can modify and reconstruct the scattered blocks directly, and the complexity is \ (\ mathcal{O}(\sqrt{n}) \)
Modifying the whole block \ (i \) can be divided into three cases:
-
\There is no \ (x \) in ([l, r] \), skip;
-
\([l, r] \) has \ (x \) and no \ (Y \), making RT [i] [y] = RT [i] [x], Val [i] [RT [i] [x] = y, RT [i] [x] = 0
-
\([l, r] \) contains \ (x \) and \ (y \). Obviously, the maximum number of different values in the sequence is \ (n + m \), in this case, the number of values in each block will be reduced by \ (1 \), and the average total complexity of violent reconstruction is \ (\ mathcal{O}((n + m)\sqrt{n}) \), which can be used directly
Time complexity \ (\ mathcal{O}((n + m) \sqrt{n}) \)
code
#include <cstdio> #include <cmath> #include <algorithm> using namespace std; const int maxn = 1e5 + 5; const int maxk = 320; int n, m; int block, tot; int st[maxk], ed[maxk]; int a[maxn], bel[maxn]; int cnt1[maxk], sum1[maxk][maxk]; int cnt2[maxn], sum2[maxk][maxn]; int rt[maxk][maxn], val[maxk][maxn], pos[maxn]; void reduce(int idx) { for (int i = st[idx]; i <= ed[idx]; i++) { a[i] = val[idx][pos[i]]; } } void build(int idx) { int cur = 0; for (int i = 1; i <= block; i++) { rt[idx][val[idx][i]] = 0; } for (int i = st[idx]; i <= ed[idx]; i++) { if (!rt[idx][a[i]]) { cur++; rt[idx][a[i]] = cur; val[idx][cur] = a[i]; } pos[i] = rt[idx][a[i]]; } } void modify(int l, int r, int x, int y) { for (int i = l; i <= r; i++) { if (a[i] == x) { sum1[bel[i]][bel[x]]--; sum1[bel[i]][bel[y]]++; sum2[bel[i]][x]--; sum2[bel[i]][y]++; a[i] = y; } } } void merge(int idx, int x, int y) { rt[idx][y] = rt[idx][x]; val[idx][rt[idx][x]] = y; rt[idx][x] = 0; } void update(int l, int r, int x, int y) { if ((x == y) || (sum2[bel[r]][x] - sum2[bel[l] - 1][x] == 0)) { return; } for (int i = bel[n]; i >= bel[l]; i--) { sum1[i][bel[x]] -= sum1[i - 1][bel[x]]; sum1[i][bel[y]] -= sum1[i - 1][bel[y]]; sum2[i][x] -= sum2[i - 1][x]; sum2[i][y] -= sum2[i - 1][y]; } if (bel[l] == bel[r]) { reduce(bel[l]); modify(l, r, x, y); build(bel[l]); for (int i = bel[l]; i <= bel[n]; i++) { sum1[i][bel[x]] += sum1[i - 1][bel[x]]; sum1[i][bel[y]] += sum1[i - 1][bel[y]]; sum2[i][x] += sum2[i - 1][x]; sum2[i][y] += sum2[i - 1][y]; } } else { reduce(bel[l]); modify(l, ed[bel[l]], x, y); build(bel[l]); reduce(bel[r]); modify(st[bel[r]], r, x, y); build(bel[r]); for (int i = bel[l] + 1; i < bel[r]; i++) { if (!sum2[i][x]) { continue; } else if (sum2[i][y]) { reduce(i); modify(st[i], ed[i], x, y); build(i); } else { sum1[i][bel[y]] += sum2[i][x]; sum1[i][bel[x]] -= sum2[i][x]; sum2[i][y] += sum2[i][x]; sum2[i][x] = 0; merge(i, x, y); } } for (int i = bel[l]; i <= bel[n]; i++) { sum1[i][bel[x]] += sum1[i - 1][bel[x]]; sum1[i][bel[y]] += sum1[i - 1][bel[y]]; sum2[i][x] += sum2[i - 1][x]; sum2[i][y] += sum2[i - 1][y]; } } } int query(int l, int r, int k) { int ans, sum = 0; if (bel[l] == bel[r]) { reduce(bel[l]); for (int i = l; i <= r; i++) { cnt2[i] = a[i]; } nth_element(cnt2 + l, cnt2 + l + k - 1, cnt2 + r + 1); ans = cnt2[l + k - 1]; for (int i = l; i <= r; i++) { cnt2[i] = 0; } return ans; } reduce(bel[l]); for (int i = l; i <= ed[bel[l]]; i++) { cnt1[bel[a[i]]]++; cnt2[a[i]]++; } reduce(bel[r]); for (int i = st[bel[r]]; i <= r; i++) { cnt1[bel[a[i]]]++; cnt2[a[i]]++; } for (int i = 1; i <= bel[100000]; i++) { if ((sum + cnt1[i] + sum1[bel[r] - 1][i] - sum1[bel[l]][i]) >= k) { for (int j = (i - 1) * block + 1; j <= i * block; j++) { if ((sum + cnt2[j] + sum2[bel[r] - 1][j] - sum2[bel[l]][j]) >= k) { for (int k = l; k <= ed[bel[l]]; k++) { cnt1[bel[a[k]]]--; cnt2[a[k]]--; } for (int k = st[bel[r]]; k <= r; k++) { cnt1[bel[a[k]]]--; cnt2[a[k]]--; } return j; } else { sum += (cnt2[j] + sum2[bel[r] - 1][j] - sum2[bel[l]][j]); } } } else { sum += (cnt1[i] + sum1[bel[r] - 1][i] - sum1[bel[l]][i]); } } } int main() { int opt, l, r, x, y, k; scanf("%d%d", &n, &m); block = sqrt(n); tot = ceil(n * 1.0 / block); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); } for (int i = 1; i < maxn; i++) { bel[i] = (i - 1) / block + 1; } for (int i = 1; i <= tot; i++) { st[i] = (i - 1) * block + 1; ed[i] = i * block; } ed[tot] = n; for (int i = 1; i <= tot; i++) { build(i); } for (int i = 1; i <= tot; i++) { for (int j = 1; j < maxk; j++) { sum1[i][j] = sum1[i - 1][j]; } for (int j = 1; j < maxn; j++) { sum2[i][j] = sum2[i - 1][j]; } for (int j = st[i]; j <= ed[i]; j++) { sum1[i][bel[a[j]]]++; sum2[i][a[j]]++; } } for (int i = 1; i <= m; i++) { scanf("%d", &opt); if (opt == 1) { scanf("%d%d%d%d", &l, &r, &x, &y); update(l, r, x, y); } else { scanf("%d%d%d", &l, &r, &k); printf("%d\n", query(l, r, k)); } } return 0; }