「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;
}
posted @ 2025-10-31 13:13  Eon_Sky  阅读(4)  评论(0)    收藏  举报