Loading

「学习笔记/刷题记录」带修莫队算法([国家集训队] 数颜色 / 维护队列)

前言

玩了半个下午的卡常,快玩出花了,我都怀疑最后可能有人会把我举报卡评测,如下图
image
只能说,inline 是个好东西,直接快了近 \(2s\)

进入正题

要知道,最普通的莫队算法是不支持修改的,前面也提到过,这个带修莫队(可持久化莫队)是后来人改进得来的(好像还改进到在线莫队了?我不清楚,错了别喷),在存储方面,带修莫队只是增加了新的一维——时间轴,代表这是第几次修改后的查询
来看看下面这道题(第一次交全RE了)

[国家集训队] 数颜色 / 维护队列

墨墨购买了一套 \(N\) 支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:
1、\(Q\ L\ R\) 代表询问你从第 \(L\) 支画笔到第 \(R\) 支画笔中共有几种不同颜色的画笔。
2、\(R\ P\ Col\) 把第 \(P\) 支画笔替换为颜色 \(Col\)
为了满足墨墨的要求,你知道你需要干什么了吗?


和【小Z的袜子】相比,这道题不用维护概率那个恶心人的东西,只需要维护数量就好了,它的 add() 和 del() 函数和一般的莫队写法都差不多,只是注意,在本题中如果某种颜色的画笔数量减为 \(0\) 了,那么 \(ans\) 就要 \(-1\);某种画笔的数量从 \(0\) 变成了 \(1\),那么 \(ans\) 就要 \(+1\)
而我们多了时间轴这意味,可以这么理解一下,如果当前正在查询的时间轴 \(T\),比你现在的时间轴 \(t\) 要大,那你就把你的 \(t\) 改到 \(T\),就是你改少了再多改写,同理,你改多了就再改回来
具体怎么改,看代码吧,其实只要搞懂莫队的思想,带修莫队也没什么能讲的
代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

const int N = 2e6 + 5;

int n, m, num, cnta, cntc, ans;
int c[N], pos[N], cnt[N], res[N];

struct xunwen {
	int l, r, t, id;
	int operator < (const xunwen &b) {
		return pos[l] == pos[b.l] ? pos[r] == pos[b.r] ? t < b.t : 
		pos[r] < pos[b.r] : pos[l] < pos[b.l];
	}
} ask[N];

struct xiugai {
	int l, r;
} change[N];

inline void add(int x) {
	ans += !cnt[x];
	++ cnt[x];
}

inline void del(int x) {
	ans -= !-- cnt[x];
}

inline void pushup(int x, int t) {
	if (ask[x].l <= change[t].l && change[t].l <= ask[x].r) {
		del(c[change[t].l]);
		add(change[t].r);
	}
	swap(change[t].r, c[change[t].l]);
}

int main() {
	n = read(), m = read();
	num = pow(n, 0.666);
	for (int i = 1; i <= n; ++ i) {
		c[i] = read();
		pos[i] = (i - 1) / num + 1;
	}
	for (int i = 1; i <= m; ++ i) {
		char op[5];
		scanf("%s", op);
		int l = read(), r = read();
		if (op[0] == 'Q') {
			ask[++ cnta].l = l;
			ask[cnta].r = r;
			ask[cnta].id = cnta;
			ask[cnta].t = cntc;
		}
		else {
			change[++ cntc].l = l;
			change[cntc].r = r;
		}
	}
	sort(ask + 1, ask + cnta + 1);
	int l = 0, r = -1, t = 0;
	for (int i = 1; i <= cnta; ++ i) {
		while (l < ask[i].l) {
			del(c[l ++]);
		}
		while (l > ask[i].l) {
			add(c[-- l]);
		}
		while (r < ask[i].r) {
			add(c[++ r]);
		}
		while (r > ask[i].r) {
			del(c[r --]);
		}
		while (t < ask[i].t) {
			pushup(i, ++ t);
		}
		while (t > ask[i].t) {
			pushup(i, t --);
		}
		res[ask[i].id] = ans;
	}
	for (int i = 1; i <= cnta; ++ i) {
		printf("%d\n", res[i]);
	}
	return 0;
}
posted @ 2023-01-06 18:58  yi_fan0305  阅读(45)  评论(0编辑  收藏  举报