AcWing 2521. 数颜色 题解

AcWing 2521. 数颜色
在基础莫队的基础上由于有修改操作,所以需要再增加一维代表时间,先按照左边界分块然后按照右边界分块然后按照时间升序排序。这里的时间的意识是当前查询位于哪次修改之后。初始没修改过的几次查询时间为0,第一次查询后的时间设为1,以此类推。对于修改操作可以看成一次删除和一次加数的操作,将需要被替换的数删去,然后将新的数加上。在指针移动的过程中,我们先将指针和待查询区间的左右边界匹配,然后再将当前时间和查询区间所在的区间匹配。在代码实现时有个小技巧:假设第 次更新操作,将 更新为 ,那么我们可以在更新完后 swap(a[pos],c[t].col),即将第 次修改的值与原数组中对应位置的值交换,这样下次如果需要撤销修改,可以直接再执行一次修改就行了(同样再进行一次swap),具体看代码。
大佬题解原地址

#include<bits/stdc++.h>

using namespace std;

const int N = 1e4 + 10, S = 1e6 + 10;

struct Query{  //记录查询操作 
	int id, l, r, t;  //多记录一维时间戳 
}q[N];

struct Modify{  //记录修改操作 
	int p, c;  //把第p支笔颜色修改为c 
}c[N];  

int n, m;
int a[N];  //记录数组
int len;
int cnt[S];
int md, mq;  //分别表示时间戳和查询的次数 
int ans[N];

int get(int x){
	return x / len;
} 

bool cmp(const Query &a, const Query &b){
	int al = get(a.l), ar = get(a.r);
	int bl = get(b.l), br = get(b.r);
	if(al != bl) return al < bl;
	if(ar != br) return ar < br;
	return a.t < b.t;  //多了一维时间戳用于排序 
}

void add(int x, int& res){
	cnt[x] ++ ;
	if(cnt[x] == 1) res ++ ;
}

void del(int x, int& res){   
	cnt[x] -- ;
	if(!cnt[x]) res -- ;
}

int main()
{

	scanf("%d%d", &n, &m);
	
	for(int i = 1; i <= n; i ++ ){
		scanf("%d", &a[i]);
	}
	
	for(int i = 0; i < m; i ++ ){
		char op[2];
		int a, b;
		scanf("%s%d%d", op, &a, &b);
		//mc是时间戳,只和修改操作有关,1表示第一次修改之后,2表示第二次修改之后
		//mq记录的是查询操作次数 
		if(*op == 'Q'){  //查询操作 
			mq ++ ;  //记录查询操作的个数
			q[mq] = {mq, a, b, md};
		}
		else{
			c[ ++ md] = {a, b};
		}
	} 
	
	len = cbrt((double)n * max(1, md)) + 1;
	sort(q + 1, q + mq + 1, cmp);
	
	for(int ll = 1, rr = 0, ts = 0, res = 0, k = 1; k <= mq; k ++ ){
		int id = q[k].id, l = q[k].l, r = q[k].r, t = q[k].t;
		
		while(rr < r) add(a[ ++ rr], res);
		while(rr > r) del(a[rr -- ], res);
		while(ll > l) add(a[ -- ll], res);
		while(ll < l) del(a[ll ++ ], res);
		while(ts < t){
			//当前ts的修改操作已经做过了,所以先ts++,再修改数组 
			ts ++ ;  
			if(c[ts].p <= rr && c[ts].p >= ll){
				del(a[c[ts].p], res);
				add(c[ts].c, res);
			}
			swap(c[ts].c, a[c[ts].p]);
		}
		while(ts > t){
			//当前ts的修改操作还没做,所以先修改数组,再ts-- 
			if(c[ts].p <= rr && c[ts].p >= ll){
				del(a[c[ts].p], res);
				add(c[ts].c, res);
			}
			swap(c[ts].c, a[c[ts].p]);	
			ts -- ;		
		}
		ans[id] = res;
	}
	
	for(int i = 1; i <= mq; i ++ ) cout<<ans[i]<<endl;
	
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值