最初分块
\(\color{black}{P4119 [Ynoi2018] 未来日记}\)
主体思路:序列分块 + 值域分块
复杂度:\(O(n(\frac{n}{b} + b))\) (大约是这个级别,需要考虑两种块长)
完整思路
考虑数列分块+值域分块
数列分块需要维护:
| \(nid_{i,j}\) | \(fid_i\) | \(f_i\) | 
|---|---|---|
| 块 \(i\) 中数字 \(j\) 的并查集的根 | 以 \(i\) 为根的并查集表示的数字 | 并查集 | 
值域分块需要维护:
| \(ncnt_{i,j}\) | \(bcnt_{i,j}\) | 
|---|---|
| 前 \(i\) 个块数字 \(j\) 的出现次数 | 前 \(i\) 个块中在值域块 \(j\) 中的个数 | 
预处理:
序列分块、值域分块块长,序列、值域每个值对应块。
每个块用 \(nid,fid\) 建立映射,\(f_i\) 连向此块与当前点值相同的第一个值(的下标),若此块中第一次出现当前点值,则考虑对 \(nid,fid\) 赋值。
显著地,扫一遍序列同时现将 \(ncnt,bcnt\) 赋上初值,再进行一遍前缀和。
修改:
碎块直接暴力赋值暴力重构,记得更新前缀和。
整块直接合并 \(x,y\) 两值,若 \(y\) 值不存在则直接将 \(x\) 并查集根节点所代表值改为 \(y\)。
有一个细节,处理整块时,需要上一个块的原前缀和信息,所以要先把信息记录下来再更新上一个块的前缀和信息。最后把所有后缀的块的信息更新一下。
查询:
碎块处理需要先访问到序列真实值,考虑开两个数组维护碎块的每个值出现次数、值域块内值个数。
查询直接先一点点跳整块(要算上碎块维护的值以及整块的值),然后一个个跳数字找到答案。
查询完毕记得清空维护碎块数组,注意要用添加的镜像操作回退,保证复杂度。
$\text{Code}$:
#include<bits/stdc++.h>
using namespace std;typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned int UIT;
typedef double DB;
typedef pair<int, int> PII;
#define IL inline
#define RE register#define fi first
#define se second
//--------------------//
//IO
IL int rd() {int ret = 0, f = 1;char ch = getchar();while (ch < '0' || ch > '9') {if (ch == '-')f = -f;ch = getchar();}while (ch >= '0' && ch <= '9')ret = ret * 10 + ch - '0', ch = getchar();return ret * f;
}
//--------------------//
const int N = 1e5 + 5, V = 1e5;
int n, m, a[N];
//--------------------//
const int LENA = 512, LENV = 512;
const int QNA = N / LENA + 5, QNV = V / LENV + 5;int acnt, al[N], ar[N], aid[N];
int vcnt, vid[N];int nid[QNA][N], fid[N], f[N];
int ncnt[QNA][N], bcnt[QNA][QNV];int find(int x) {return f[x] == x ? x : f[x] = find(f[x]);
}void init_bl(int id) {for (int i = al[id]; i <= ar[id]; i++) {fid[i] = a[i], ncnt[id][a[i]]++, bcnt[id][vid[a[i]]]++;if (!nid[id][a[i]])nid[id][a[i]] = i, fid[i] = a[i];f[i] = nid[id][a[i]];// printf("f %d %d %d\n", i, f[i], nid[id][a[i]]);}return;
}void init() {for (int i = 1; i <= n; i++)aid[i] = (i - 1) / LENA + 1;acnt = aid[n];for (int i = 1; i <= acnt; i++)al[i] = (i - 1) * LENA + 1, ar[i] = i * LENA;ar[acnt] = n;for (int i = 1; i <= V; i++)vid[i] = (i - 1) / LENV + 1; vcnt = vid[V];for (int i = 1; i <= acnt; i++) {init_bl(i);for (int j = 1; j <= V; j++)ncnt[i][j] += ncnt[i - 1][j];for (int j = 1; j <= vcnt; j++)bcnt[i][j] += bcnt[i - 1][j];}
}int temp;void modify_temp(int id, int x, int y) {ncnt[id][x] -= temp, ncnt[id][y] += temp;bcnt[id][vid[x]] -= temp, bcnt[id][vid[y]] += temp;return;
}void modify_pic(int id, int l, int r, int x, int y) {if (!nid[id][x])return;for (int i = al[id]; i <= ar[id]; i++)a[i] = fid[find(i)];/*printf("MTes:\n");for (int i = al[id]; i <= ar[id]; i++)printf("%d %d\n", i, a[i]);*/for (int i = l; i <= r; i++)if (a[i] == x)temp++, a[i] = y;fid[nid[id][x]] = 0, nid[id][x] = 0;for (int i = al[id]; i <= ar[id]; i++) {if (a[i] == x) {if (!nid[id][x])nid[id][x] = i, fid[i] = x;f[i] = nid[id][x];} else if (a[i] == y) {if (!nid[id][y])nid[id][y] = i, fid[i] = y;f[i] = nid[id][y];}}return;	
}void modify_bl(int id, int x, int y) {if (!nid[id][x])return;if (!nid[id][y]) {nid[id][y] = nid[id][x];fid[nid[id][y]] = y;nid[id][x] = 0;return;}f[nid[id][x]] = nid[id][y];fid[nid[id][x]] = nid[id][x] = 0;// printf("done!\n");return;
}void modify(int l, int r, int x, int y) {temp = 0;if (aid[l] == aid[r]) {int id = aid[l];modify_pic(id, l, r, x, y);for (int i = id; i <= acnt; i++)modify_temp(i, x, y);return;}modify_pic(aid[l], l, ar[aid[l]], x, y);for (int tem, i = aid[l] + 1; i < aid[r]; i++) {modify_bl(i, x, y);tem = ncnt[i][x] - ncnt[i - 1][x];modify_temp(i - 1, x, y);temp += tem;}modify_temp(aid[r] - 1, x, y);modify_pic(aid[r], al[aid[r]], r, x, y);for (int i = aid[r]; i <= acnt; i++)modify_temp(i, x, y);return;
}int temn[N], temb[QNV];int get_ans(int k, int l, int r) {int now = 1;while (k > temb[now] + bcnt[r][now] - bcnt[l - 1][now])k -= temb[now] + bcnt[r][now] - bcnt[l - 1][now], now++;for (int i = (now - 1) * LENV + 1; i <= min(now * LENV, V); i++) {k -= temn[i] + ncnt[r][i] - ncnt[l - 1][i];if (k <= 0)return i;}return 114514;
}int query(int k, int l, int r) {int lid = aid[l], rid = aid[r], res = 19198;if (lid == rid) {for (int i = l; i <= r; i++)a[i] = fid[find(i)], temn[a[i]]++, temb[vid[a[i]]]++; // printf("?%d %d %d %d\n", i, a[i], f[i], fid[f[i]]);res = get_ans(k, 1, 0);for (int i = l; i <= r; i++)temn[a[i]]--, temb[vid[a[i]]]--;return res;}for (int i = l; i <= ar[lid]; i++)a[i] = fid[find(i)], temn[a[i]]++, temb[vid[a[i]]]++;for (int i = al[rid]; i <= r; i++)a[i] = fid[find(i)], temn[a[i]]++, temb[vid[a[i]]]++;/*printf("Qtes:\n");for (int i = l; i <= ar[lid]; i++)printf("%d(f:%d, i:%d) ", a[i], f[i], i);puts("");for (int i = al[rid]; i <= r; i++)printf("%d(f:%d, i:%d) ", a[i], f[i], i);puts("");*/res = get_ans(k, lid + 1, rid - 1);for (int i = l; i <= ar[lid]; i++)a[i] = fid[find(i)], temn[a[i]]--, temb[vid[a[i]]]--;for (int i = al[rid]; i <= r; i++)a[i] = fid[find(i)], temn[a[i]]--, temb[vid[a[i]]]--;return res;
}
//--------------------//
int main()
{n = rd(), m = rd();for (int i = 1; i <= n; i++)a[i] = rd();init();for (int op, l, r, x, y, i = 1; i <= m; i++) {op = rd(), l = rd(), r = rd(), x = rd();if (op == 1) {y = rd();if (x != y)modify(l, r, x, y);} else {printf("%d\n", query(x, l, r));}}return 0;
}