人人本着希望之名

望月悲叹的最初分块

严格强于区间 rank, 直接考虑分块.

平凡的 \(\mathcal O(n\sqrt{n\log n})\) 做法缺点在于二分这个东西完全用不到分块容易预处理的优秀性质. 本题的值域很小, 二分直接扔掉.

相比较而言, 值域分块和序列分块契合度更高, 考虑值域分块.

\(g_{i, j}\) 表示前 \(i\) 块有 \(g_{i, j}\) 个数值为 \(j\), \(G_{i, j}\) 表示前 \(i\) 块有 \(G_{i, j}\) 个数值域在第 \(j\) 块.

查询时先把散块的信息算出来, 然后在值域块上跳, 单次查询块内信息是 \(\mathcal O(1)\) 的, 整体复杂度 \(\mathcal O((n+m)\sqrt n)\).

然后考虑修改. 发现这个修改严格弱于 loj516, 散块暴力重构, 整块打 tag. 整体的复杂度还是 \(\mathcal O((n + m)\sqrt n)\). 代码还没有, 别急. 有代码了.

#include <bits/stdc++.h>
#define pb emplace_back
#define fir first
#define sec second

using i64 = long long;
using pii = std::pair<int, int>;

const int maxn = 1e5 + 5;
const int T = 330;
int n, m, S, V, mx, a[maxn], t, vt, L[T], R[T], pos[maxn], vL[T], vR[T];
int vpos[maxn], val[maxn], pre[maxn], rec[T][maxn], g[T][maxn], G[T][maxn];

struct Query {
	int op, l, r, x, y;
	Query() {
		op = l = r = x = y = 0;
	} 
	Query(int op, int l, int r, int x = 0, int y = 0) : op(op), l(l), r(r), x(x), y(y) {}
} Q[maxn];

int read() {
	int s = 0;
	char c = getchar();
	for(;c < '0'||c > '9';c = getchar());
	for(;c >= '0'&&c <= '9';c = getchar())
		s = (s << 1) + (s << 3) + (c ^ '0');
	return s;
}

void write(int x) {
	if(x > 9)
		write(x / 10);
	putchar(x % 10 + '0');
	return ;
}

int find(int x) {
	return x == pre[x] ? x : pre[x] = find(pre[x]); 
}

void merge(int blo, int x, int y) {
	if(!rec[blo][x])
		return ;
	if(!rec[blo][y])
		val[rec[blo][y] = rec[blo][x]] = y;
	else
		pre[rec[blo][x]] = pre[rec[blo][y]];
	return rec[blo][x] = 0, void();
}

void rebuild(int blo, int l, int r, int x, int y) {
	for(int i = L[blo];i <= R[blo];++ i)
		a[i] = val[find(i)], rec[blo][a[i]] = 0;
	for(int i = l;i <= r;++ i)
		if(a[i] == x)
			a[i] = y;
	for(int i = L[blo];i <= R[blo];++ i) {
		if(!rec[blo][a[i]])
			rec[blo][a[i]] = pre[i] = i, val[i] = a[i];
		else
			pre[i] = rec[blo][a[i]];
	}
	return ;
}

void modify(int l, int r, int x, int y) {
	if(x == y)
		return ;
	const int p = pos[l], q = pos[r], vpx = vpos[x], vpy = vpos[y];
	int sum = 0, tmp = 0;
	if(p == q) {
		for(int i = l;i <= r;++ i)
			sum += val[find(i)] == x;
		if(!sum)
			return ;
		for(int i = p;i <= t;++ i)
			g[i][x] -= sum, G[i][vpx] -= sum, g[i][y] += sum, G[i][vpy] += sum;
		return rebuild(p, l, r, x, y);
	}
	for(int i = l;i <= R[p];++ i)
		sum += val[find(i)] == x;
	if(sum)
		rebuild(p, l, R[p], x, y);
	for(int i = p;i < q;++ i)
		sum += tmp, G[i][vpx] -= sum, G[i][vpy] += sum, tmp = g[i + 1][x] - g[i][x], g[i][x] -= sum, g[i][y] += sum;
	tmp = 0;
	for(int i = L[q];i <= r;++ i)
		if(val[find(i)] == x)
			++ sum, ++ tmp;
	for(int i = q;i <= t;++ i)
		G[i][vpx] -= sum, G[i][vpy] += sum, g[i][x] -= sum, g[i][y] += sum;
	for(int i = p + 1;i < q;++ i)
		merge(i, x, y);
	if(tmp)
		rebuild(q, L[q], r, x, y); 
	return ;
}

int buc[maxn], vbuc[maxn];

int query(int l, int r, int k) {
	const int p = pos[l], q = pos[r];
	if(k > r - l + 1)
		return -1;
	if(p == q) {
		for(int i = l, x;i <= r;++ i)
			++ buc[x = val[find(i)]], ++ vbuc[vpos[x]];
		for(int blo = 1;blo <= vt;++ blo) {
			if(k > vbuc[blo])
				k -= vbuc[blo];
			else
				for(int z = vL[blo];z <= vR[blo];++ z) {
					if(k > buc[z])
						k -= buc[z];
					else {
						for(int i = l, x;i <= r;++ i)
							-- buc[x = val[find(i)]], -- vbuc[vpos[x]];
						return z;
					}
				}
		}
		return 0;
	}
	for(int i = l, x;i <= R[p];++ i)
		++ buc[x = val[find(i)]], ++ vbuc[vpos[x]];
	for(int i = L[q], x;i <= r;++ i)
		++ buc[x = val[find(i)]], ++ vbuc[vpos[x]];
	for(int blo = 1;blo <= vt;++ blo) {
		if(k > G[q - 1][blo] - G[p][blo] + vbuc[blo])
			k -= G[q - 1][blo] - G[p][blo] + vbuc[blo];
		else
			for(int z = vL[blo];z <= vR[blo];++ z) {
				if(k > g[q - 1][z] - g[p][z] + buc[z])
					k -= g[q - 1][z] - g[p][z] + buc[z];
				else {
					for(int i = l, x;i <= R[p];++ i)
						-- buc[x = val[find(i)]], -- vbuc[vpos[x]];
					for(int i = L[q], x;i <= r;++ i)
						-- buc[x = val[find(i)]], -- vbuc[vpos[x]];
					return z;
				}
			}
	}
	return 0;
}

int main() {
	n = read(), m = read();
	S = std::sqrt(n);
	t = (n - 1) / S + 1;
	for(int i = 1;i <= t;++ i)
		L[i] = R[i - 1] + 1, R[i] = R[i - 1] + S;
	R[t] = n;
	for(int i = 1;i <= t;++ i)
		for(int j = L[i];j <= R[i];++ j) {
			V = std::max(V, a[j] = read());
			if(!rec[i][a[j]])
				pre[j] = rec[i][a[j]] = j, val[j] = a[j];
			else
				pre[j] = rec[i][a[j]];
			pos[j] = i;
		}
	for(int i = 1;i <= m;++ i) {
		Q[i].op = read();
		Q[i].l = read();
		Q[i].r = read();
		Q[i].x = read();
		if(Q[i].op == 1)
			Q[i].y = read(), V = std::max(V, Q[i].x), V = std::max(V, Q[i].y);
	}
	mx = V;
	V = std::sqrt(V);
	vt = (mx - 1) / V + 1;
	for(int i = 1;i <= vt;++ i)
		vL[i] = vR[i - 1] + 1, vR[i] = vR[i - 1] + V;
	vR[vt] = mx;
	for(int i = 1;i <= vt;++ i)
		for(int j = vL[i];j <= vR[i];++ j)
			vpos[j] = i;
	for(int i = 1;i <= t;++ i) {
		for(int j = L[i];j <= R[i];++ j)
			++ g[i][a[j]], ++ G[i][vpos[a[j]]];
		for(int j = 1;j <= mx;++ j) {
			g[i][j] += g[i - 1][j];
			if(j <= vt)
				G[i][j] += G[i - 1][j];
		}
	}
	for(int i = 1;i <= m;++ i)
		if(Q[i].op == 1)
			modify(Q[i].l, Q[i].r, Q[i].x, Q[i].y);
		else
			write(query(Q[i].l, Q[i].r, Q[i].x)), putchar('\n');
	return 0;
}
posted @ 2023-06-08 11:35  ImALAS  阅读(37)  评论(0编辑  收藏  举报