【ybt金牌导航6-4-2】【luogu P1903】数颜色 / 维护队列(带修莫队)

数颜色 / 维护队列

题目链接:ybt金牌导航6-4-2 / luogu P1903

题目大意

要你维护一个序列,可能会改变一个位置的数,还有询问一个区间内有多少种数。

思路

这道题有多种做法,然后我们考虑用带修莫队来做。

莫队是两个指针第一个按块搞,第二个就按个搞。
那我们带修的话我们就可以加上一个时间指针。

那也是一个道理,我们就也是分成块,然后第一个排序按左边的块排,第二个排序按右边的块排,接着就按着时间排。
然后就每次三个指针移动一下就可以了。

然后至于时间指针的移动,把位置 x x x a a a 换成 b b b,就相当于把 a a a 的贡献消除,加上 b b b 的贡献。

然后就差不多了,我们最优会选取 n 2 3 n^{\frac{2}{3}} n32 的长度,这样复杂度是 n 5 3 n^{\frac{5}{3}} n35 的。、
(搞这个长度直接用 pow \text{pow} pow 函数)

代码

#include<cmath>
#include<cstdio>
#include<algorithm>

using namespace std;

struct node {
	int t, x, y, num, ans;
}a[140001];
int n, m, x, y, block[140001], t, col[140001];
int fr[140001], to[140001], lst[140001], sz;
int q, tim, pl[140001], num[1000001], ans;
bool in[140001];
char op;

bool cmp(node x, node y) {//莫队的排序
	if (block[x.x] != block[y.x]) return block[x.x] < block[y.x];
	if (block[x.y] != block[y.y]) return block[x.y] < block[y.y];
	return x.t < y.t;
}

void clac(int p) {//更新当前位置的值
	if (in[p]) {
		num[col[p]]--;
		if (!num[col[p]]) ans--;
	}
	else {
		num[col[p]]++;
		if (num[col[p]] == 1) ans++;
	}
	in[p] ^= 1;
}

void change(int p, int t) {//换数
	if (in[p]) {
		clac(p);
		col[p] = t;
		clac(p);
	}
	else col[p] = t;
}

bool cmp1(node x, node y) {
	return x.num < y.num;
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &lst[i]);
		col[i] = lst[i];
	}
	
	sz = pow(n, 2.0 / 3);
	for (int i = 1; i <= n; i++)
		block[i] = (i - 1) / sz + 1;
	
	for (int i = 1; i <= m; i++) {
		op = getchar();
		while (op != 'Q' && op != 'R') op = getchar();
		
		if (op == 'Q') {
			scanf("%d %d", &x, &y);
			a[++q] = (node){tim, x, y, i, 0};
		}
		else {
			scanf("%d %d", &x, &y);
			pl[++tim] = x;
			fr[tim] = lst[x];
			to[tim] = y;
			lst[x] = y;
		}
	}
	
	sort(a + 1, a + q + 1, cmp);
	t = 0; x = 1; y = 0;
	for (int i = 1; i <= q; i++) {
		while (t < a[i].t) t++, change(pl[t], to[t]);//三个指针分别移动
		while (t > a[i].t) change(pl[t], fr[t]), t--;
		while (x < a[i].x) clac(x), x++;
		while (x > a[i].x) x--, clac(x);
		while (y < a[i].y) y++, clac(y);
		while (y > a[i].y) clac(y), y--;
		a[i].ans = ans;
	}
	
	sort(a + 1, a + q + 1, cmp1);
	for (int i = 1; i <= q; i++) printf("%d\n", a[i].ans);
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值