LUOGU P4119 Ynoi2018 未来日记
题意
有一个长度为 \(n\) 的序列,共 \(m\) 次操作:
1 l r x y
,把区间 \([l, r]\) 内所有 \(x\) 变成 \(y\);2 l r k
,查询区间 \([l, r]\) 内第 \(k\) 小值.
\(1\le n, m\le 10^5\),任何时刻 \(1\le a_i\le 10^5\)
题解
对于这种复杂的修改操作,我们直接考虑分块
对序列和值域同时分块,令 \(sum_{i, v}\) 表示前 \(i\) 块内值 \(v\) 的个数,\(blsum_{i, j}\) 表示序列前 \(i\) 块内值在值域第 \(j\) 块内的个数
考虑查询,对于散块我们每次查询构造 \(sctsum_{v}\) 表示在散块中值为 \(v\) 的个数,\(sctblsum_{i}\) 表示散块中值在值域第 \(i\) 块内的个数
查询时我们对整块差分再加上散块的贡献,即可 \(\Theta(1)\) 地查询区间 \([l, r]\) 内值为 \(v\) 的个数和区间内值在值域第 \(i\) 块中的个数
先枚举 \(k\) 小值在值域中的哪一块,再枚举 \(k\) 小值是该块中的哪一个值,一次查询的时间复杂度为 \(\Theta(\sqrt{n})\)
考虑修改,对于散块直接暴力修改即可
对于每个整块独立地给块内每一个值一个标号,颜色相同的有相同的标号,还需要维护值对标号的映射和值对标号的映射
把 \(x\) 变成 \(y\) 时的两种情况:
- 块内没有 \(y\)
将 \(x\) 对应的标号映射到 \(y\),将 \(y\) 映射到原本 \(x\) 对应的标号,将 \(x\) 的映射清空 - 块内有 \(y\)
不太好处理,直接像对散块一样暴力修改
考虑这样做的时间复杂度
暴力修改的次数不多于所有块内不同值个数的和,一开始有 \(\mathcal{O}(n)\) 个,每修改一次整块不会增加,只有散块可能增加1,所以总共暴力修改 \(\mathcal{O}(n+m)\) 次,总的暴力修改复杂度即为 \(\mathcal{O}((n+m)\sqrt{n})\)
每次暴力修改前都要 "rebuild" 该块,即重新分配标号,重新生成两个映射
每次操作散块都要前 "reset" 该块,即对于每个值用标号映射得到新的值
总的时间复杂度为 \(\mathcal{O}((n+m)\sqrt{n})\)
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100000, maxvs = 316;
const int maxs = 360, maxc = 278;
int cnt;
int a[maxn + 5];
int lp[maxc + 5], rp[maxvs + 5];
int bl[maxn + 5];
int vlp[maxvs + 5];
int vbl[maxn + 5];
int sum[maxc + 5][maxn + 5];
int blsum[maxc + 5][maxvs + 5];
int rep[maxc + 5][maxn + 5];
int col[maxc + 5][maxs + 5];
int id[maxn + 5];
int ccnt[maxc + 5];
void buildUnion(int bid) {
for (int i = 1; i <= ccnt[bid]; i++)
rep[bid][col[bid][i]] = 0;
ccnt[bid] = 0;
for (int i = lp[bid]; i <= rp[bid]; i++) {
if (rep[bid][a[i]] == 0) {
rep[bid][a[i]] = ++ccnt[bid];
col[bid][ccnt[bid]] = a[i];
}
id[i] = rep[bid][a[i]];
}
}
void reset(int bid) {
for (int i = lp[bid]; i <= rp[bid]; i++)
a[i] = col[bid][id[i]];
}
void build(int n) {
int siz = min(n, int(sqrt(1.3 * n)));
cnt = ceil(1. * n / siz);
for (int i = 1; i <= n; i++)
bl[i] = (i - 1) / siz + 1;
for (int i = 1; i <= cnt; i++) {
lp[i] = (i - 1) * siz + 1;
rp[i] = min(n, i * siz);
}
for (int i = 1; i <= maxn; i++)
vbl[i] = (i - 1) / maxvs + 1;
for (int i = 1; i <= maxvs + 1; i++)
vlp[i] = (i - 1) * maxvs + 1;
for (int i = 1; i <= cnt; i++) {
for (int j = 1; j <= maxn; j++)
sum[i][j] = sum[i - 1][j];
for (int j = 1; j <= maxvs + 1; j++)
blsum[i][j] = blsum[i - 1][j];
for (int j = lp[i]; j <= rp[i]; j++) {
sum[i][a[j]]++;
blsum[i][vbl[a[j]]]++;
}
buildUnion(i);
}
}
int tmp[maxn + 5], bltmp[maxvs + 5];
void add(int l, int r, int delta) {
for (int i = l; i <= r; i++) {
tmp[a[i]] += delta;
bltmp[vbl[a[i]]] += delta;
}
}
int query(int l, int r, int k) {
int lb = bl[l], rb = bl[r];
int res;
if (lb == rb) {
reset(lb);
copy(a + l, a + r + 1, tmp + l);
nth_element(tmp + l, tmp + l + k - 1, tmp + r + 1);
res = tmp[l + k - 1];
fill(tmp + l, tmp + r + 1, 0);
} else {
reset(lb);
reset(rb);
add(l, rp[lb], 1);
add(lp[rb], r, 1);
int i = 1, delta;
for (; k - (delta = bltmp[i] + blsum[rb - 1][i] - blsum[lb][i]) > 0;
i++)
k -= delta;
int j = vlp[i];
for (; k - (delta = tmp[j] + sum[rb - 1][j] - sum[lb][j]) > 0; j++)
k -= delta;
res = j;
add(l, rp[lb], -1);
add(lp[rb], r, -1);
}
return res;
}
void change(int l, int r, int x, int y, int bid) {
int chcnt = 0;
for (int i = l; i <= r; i++) {
if (a[i] == x) {
a[i] = y;
chcnt++;
}
}
sum[bid][x] -= chcnt;
sum[bid][y] += chcnt;
blsum[bid][vbl[x]] -= chcnt;
blsum[bid][vbl[y]] += chcnt;
}
void changeBlock(int l, int r, int x, int y, int bid) {
reset(bid);
change(l, r, x, y, bid);
buildUnion(bid);
}
void modify(int l, int r, int x, int y) {
int lb = bl[l], rb = bl[r];
if (x == y || sum[rb][x] - sum[lb - 1][x] == 0)
return;
for (int i = cnt; i >= lb; i--) {
sum[i][x] -= sum[i - 1][x];
sum[i][y] -= sum[i - 1][y];
blsum[i][vbl[x]] -= blsum[i - 1][vbl[x]];
blsum[i][vbl[y]] -= blsum[i - 1][vbl[y]];
}
if (lb == rb)
changeBlock(l, r, x, y, lb);
else {
changeBlock(l, rp[lb], x, y, lb);
changeBlock(lp[rb], r, x, y, rb);
for (int i = lb + 1; i < rb; i++) {
if (sum[i][x] == 0)
continue;
if (sum[i][y] == 0) {
col[i][rep[i][x]] = y;
swap(rep[i][y], rep[i][x]);
blsum[i][vbl[y]] += sum[i][x];
blsum[i][vbl[x]] -= sum[i][x];
sum[i][y] = sum[i][x];
sum[i][x] = 0;
} else
changeBlock(lp[i], rp[i], x, y, i);
}
}
for (int i = lb; i <= cnt; i++) {
sum[i][x] += sum[i - 1][x];
sum[i][y] += sum[i - 1][y];
blsum[i][vbl[x]] += blsum[i - 1][vbl[x]];
blsum[i][vbl[y]] += blsum[i - 1][vbl[y]];
}
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
build(n);
for (int i = 1; i <= m; i++) {
int op, l, r;
scanf("%d%d%d", &op, &l, &r);
if (op == 1) {
int x, y;
scanf("%d%d", &x, &y);
modify(l, r, x, y);
} else {
int k;
scanf("%d", &k);
printf("%d\n", query(l, r, k));
}
}
}
不忘初心 砥砺前行