「Note」Ynoi 系列大分块
最初分块
\(\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;
}

浙公网安备 33010602011771号