「Ynoi2018」未来日记
依旧分块。
值域上老套路了,对于这种值域小,区间将一个值赋为另一个值又跟个数相关的,直接上序列分块 + 并查集维护,问题在于要查第 \(k\) 小值,第一,不能分成若干个段单独考虑,第二,值域上肯定不能只知道个某个值有多少个就行了。
对于第一个问题,我们可以将分块维护的东西改成前缀和,定义 \(sum_{i, v}\) 表示前 \(i\) 个块中,值为 \(v\) 的元素有多少个,修改 \((l, r, x, y)\) 时从左向右累加一个 \(tmp\) 变量表示 \(x/y\) 的数量变化值即可修改,暴力扫 \([bel_r, bel_n]\) 也是 \(O(\sqrt{N})\) 的,时间没问题。
对于第二个问题,我们得在值域上做点什么加速查询且不影响复杂度,由于修改要修改 \(O(\sqrt{N})\) 次,而查询只查询 \(O(1)\) 次,容易想到类似 \(O(1) - O(\sqrt{N})\) 的修改 - 查询,定义 \(sumblk_{i, j}\) 表示前 \(i\) 个块中,值在块 \(j\)(此处为值域块)中的元素个数,对于修改跟 \(sum\) 同理,接下来讲讲查询。
有了 \(sumblk\),我们想想在查询第 \(k\) 小值时怎么加速。可以利用跟追忆查询类似的想法,先定位到答案所在块,再暴力找。我们先提取出\(c_j\) 表示区间 \([l, r]\) 中值为 \(j\) 的元素个数,\(sblk_j\) 表示区间 \([l, r]\) 中值在块 \(j\) 中的元素个数,然后暴力从小到大扫 \(sblk\) 并更新查询的 \(k\) 的值:\(k \leftarrow k - sblk_j\),找到第一个使 \(k - sblk_j \leq 0\) 的块 \(j\),说明第 \(k\) 小值在块 \(j\) 中,于是暴力扫块 \(j\) 求答案即可。
然后补充下并查集怎么维护,此题值个数另有维护,所以不考虑 \(siz\) 只考虑怎么查某个位置的值为多少。
定义 \(fa_i\) 表示序列第 \(i\) 个数在并查集上的父亲,再定义 \(rt[i][j]\) 表示块 \(i\) 中,值为 \(j\) 的元素并查集上根是谁,当整块 \(i\) 更新 \((x, y)\) 时,分两种情况:
- \(rt_{i, y} \leftarrow rt_{i, x}, a_{rt_{i, x}} \leftarrow y\),若块 \(i\) 中不存在值为 \(y\) 的元素,即 \(rt_{i, y} = 0\)
- \(fa_{rt_{i, x}} \leftarrow rt_{i, y}\),若块 \(i\) 中存在值为 \(y\) 的元素,即 \(rt_{i, y} \neq 0\)
需要注意的是,此题中不能打启发式合并,但可以打路径压缩。
现在来分析时间复杂度,\(O(N \sqrt{N})\) 是跑不掉的,然后并查集看似是 \(O(\log N)\) 的,但我们发现,并查集只有在散块修改查询时使用了 \(find(x)\) 函数,同时因为是重构整个块,是一次性使用完,将整个并查集变成了一个菊花图,均摊 \(O(\sqrt{N})\),散块共计 \(O(N)\) 次修改,所以总计 \(O(N \sqrt{N})\),整块修改只使用了 \(merge(x, y)\) 函数,\(O(1)\),整块共计 \(O(N \sqrt{N})\) 次修改,总计 \(O(N \sqrt{N})\)。
综上,时间复杂度 \(O(N \sqrt{N})\),空间复杂度 \(O(N \sqrt{N})\),此题块长开 \(\sqrt{N}\) 会 \(\text{MLE}\),将块长调大一些即可。
/*
address:https://www.luogu.com.cn/problem/P4119
AC 2026/8/4 15:39
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
const int B = 700, Bv = 320;
int n, q;
int a[N];
int cnt[N / B + 5][N], sum[N / B + 5][N], sumblk[N / B + 5][N / Bv + 5];
int L[N / B + 5], R[N / B + 5], Lv[N / Bv + 5], Rv[N / Bv + 5], bel[N], belv[N];
int siz, blk, bv;
inline void read(int& x) {
x = 0;
char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c <= '9' && c >= '0') x = x * 10 + c - '0', c = getchar();
}
int fa[N], rt[N / B + 5][N];
inline int init(int n) { for (int i = 1;i <= n;++i) fa[i] = i; }
inline int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
int b[N], m;
inline void update_scatter(int i, int l, int r, int x, int y) {
m = 0;
int tmp = 0;
rt[i][x] = rt[i][y] = 0;
for (int j = L[i];j <= R[i];++j) {
a[j] = a[find(j)];
if (a[j] == x || a[j] == y) b[++m] = j;
}
for (int j = l;j <= r;++j)
if (a[j] == x) a[j] = y, ++tmp;
for (int j = 1;j <= m;++j) fa[b[j]] = b[j];
for (int j = 1;j <= m;++j)
if (!rt[i][a[b[j]]]) rt[i][a[b[j]]] = b[j];
else fa[b[j]] = rt[i][a[b[j]]];
cnt[i][x] -= tmp, cnt[i][y] += tmp;
for (int j = i;j <= blk;++j) {
sum[j][x] -= tmp, sum[j][y] += tmp;
sumblk[j][belv[x]] -= tmp, sumblk[j][belv[y]] += tmp;
}
}
int c[N], sblk[N];
inline int query(int l, int r, int k) {
int s = bel[l], t = bel[r];
for (int i = l;i <= min(R[s], r);++i) ++c[a[i] = a[find(i)]], ++sblk[belv[a[i]]];
if (s != t) for (int i = L[t];i <= r;++i) ++c[a[i] = a[find(i)]], ++sblk[belv[a[i]]];
int b;
for (int i = 1;i <= bv;++i) {
k -= sblk[i] + (s == t ? 0 : sumblk[t - 1][i] - sumblk[s][i]);
if (k <= 0) {
k += sblk[i] + (s == t ? 0 : sumblk[t - 1][i] - sumblk[s][i]);
b = i;
break;
}
}
for (int i = Lv[b];i <= Rv[b];++i) {
k -= c[i] + (s == t ? 0 : sum[t - 1][i] - sum[s][i]);
if (k <= 0) {
for (int i = l;i <= min(R[s], r);++i) --c[a[i]], --sblk[belv[a[i]]];
if (s != t) for (int i = L[t];i <= r;++i) --c[a[i]], --sblk[belv[a[i]]];
return i;
}
}
}
int main() {
read(n), read(q);
for (int i = 1;i <= n;++i) read(a[i]), fa[i] = i;
siz = B;
blk = (n + siz - 1) / siz;
bv = (N - 5 + 316 - 1) / 316;
for (int i = 1;i <= n;++i) bel[i] = (i + siz - 1) / siz;
for (int i = 1;i <= N - 5;++i) belv[i] = (i + 316 - 1) / 316;
for (int i = 1;i <= blk;++i) L[i] = (i - 1) * siz + 1, R[i] = min(n, i * siz);
for (int i = 1;i <= bv;++i) Lv[i] = (i - 1) * 316 + 1, Rv[i] = min(N - 5, i * 316);
for (int i = 1;i <= blk;++i) {
for (int j = L[i];j <= R[i];++j) {
if (!rt[i][a[j]]) rt[i][a[j]] = j;
else fa[j] = rt[i][a[j]];
++cnt[i][a[j]];
++sumblk[i][belv[a[j]]];
}
for (int v = 1;v <= N - 5;++v) sum[i][v] = sum[i - 1][v] + cnt[i][v];
for (int bel = 1;bel <= bv;++bel) sumblk[i][bel] += sumblk[i - 1][bel];
}
while (q--) {
int op, l, r;read(op), read(l), read(r);
if (op == 1) {
int x, y;read(x), read(y);
if (x == y) continue;
int s = bel[l], t = bel[r];
update_scatter(s, l, min(R[s], r), x, y);
int tmp = 0;
for (int i = s + 1;i < t;++i) {
if (rt[i][x]) {
if (!rt[i][y]) rt[i][y] = rt[i][x], a[rt[i][x]] = y;
else fa[rt[i][x]] = rt[i][y];
rt[i][x] = 0;
tmp += cnt[i][x], cnt[i][y] += cnt[i][x], cnt[i][x] = 0;
}
sum[i][x] -= tmp, sum[i][y] += tmp;
sumblk[i][belv[x]] -= tmp, sumblk[i][belv[y]] += tmp;
}
for (int i = t;i <= blk;++i) {
sum[i][x] -= tmp, sum[i][y] += tmp;
sumblk[i][belv[x]] -= tmp, sumblk[i][belv[y]] += tmp;
}
if (s < t) update_scatter(t, L[t], r, x, y);
}
else {
int k;read(k);
printf("%d\n", query(l, r, k));
}
}
return 0;
}

浙公网安备 33010602011771号