带修改的莫队

参考博客:莫队算法-从入门到黑题

带修改的莫队:离线修改区间查询,在原来的基础上再加上一个“时间戳”,而查询操作的时间戳沿用之前最近的修改操作的时间戳。跑主算法时定义当前时间戳为 time ,对于每个查询操作,如果当前时间戳相对太大了,说明已进行的修改操作比要求的多,就把之前改的改回来,反之往后改。只有当当前区间和查询区间左右端点、时间戳均重合时,才认定区间完全重合,此时的答案才是本次查询的最终答案。

就是再弄一指针,在修改操作上跳来跳去,如果当前修改多了就改回来,改少了就改过去,直到次数恰当为止。

这样,我们当前区间的移动方向从四个 ( [ l − 1 , r ] 、 [ l + 1 , r ] 、 [ l , r − 1 ] 、 [ l , r + 1 ] ) ([l−1,r]、[l+1,r]、[l,r−1]、[l,r+1]) ([l1,r][l+1,r][l,r1][l,r+1])变成了六个 ( [ l − 1 , r , t ] 、 [ l + 1 , r , t ] 、 [ l , r − 1 , t ] 、 [ l , r + 1 , t ] 、 [ l , r , t − 1 ] 、 [ l , r , t + 1 ] ) ([l−1,r,t]、[l+1,r,t]、[l,r−1,t]、[l,r+1,t]、[l,r,t−1]、[l,r,t+1]) ([l1,r,t][l+1,r,t][l,r1,t][l,r+1,t][l,r,t1][l,r,t+1])


带修改的莫队排序

相比于普通的莫队来说,加了个“时间戳”

bool cmp(node a1,node a2) {
	if(pos[a1.l] != pos[a2.l]) return pos[a1.l] < pos[a2.l];
	else if(pos[a1.r] != pos[a2.r]) return pos[a1.r] < pos[a2.r];
	else return a1.time < a2.time;
}

指针移动

移动完左右端点后,判断当前的时间节点是否等于当前的查询的区间时间点。

void work() {
	int l = 0, r = 0,now = 0,time = 0;
	
	for(int i=1;i<=m;i++) {
		int ql = pen[i].l;
		int qr = pen[i].r;
		int qt = pen[i].time;
		
		while(l < ql) {
			cnt[a[l]] --;
			if(cnt[a[l]] == 0) now --;
			l ++;
		}
		
		while(l > ql) {
			l --;
			cnt[a[l]] ++;
			if(cnt[a[l]] == 1) now ++;
		}
		
		while(r < qr) {
			r ++;
			cnt[a[r]] ++;
			if(cnt[a[r]] == 1) now ++;
		}
		
		while(r > qr) {
			cnt[a[r]] --;
			if(cnt[a[r]] == 0) now --;
			r --;
		}
		
		while(time < qt) {
			time ++;
			int p = color[time].p;
			int c = color[time].c;
			if(p >= l && p <= r) {
				cnt[a[p]] --;
				if(cnt[a[p]] == 0) now --;
				
				cnt[c] ++;
				if(cnt[c] == 1) now ++;
				
			}
			
			swap(a[p],color[time].c);
		}
		
		while(time > qt) {
			int p = color[time].p;
			int c = color[time].c;
			if(p >= l && p <= r) {
				cnt[a[p]] --;
				if(cnt[a[p]] == 0) now --;
				
				cnt[c] ++;
				if(cnt[c] == 1) now ++;
			}
			
			swap(a[p],color[time].c);
			time --;
		}
		
		res[pen[i].id] = now;
	}
}

题目:P1903 [国家集训队] 数颜色 / 维护队列

题意:购买了一套 N N N 支彩色画笔(其中有些颜色可能相同),摆成一排。存在以下操作

  • Q   L   R Q\ L\ R Q L R 代表询问你从第 L L L 支画笔到第 R R R 支画笔中共有几种不同颜色的画笔。

  • R   P   C o l R\ P\ Col R P Col 把第 P P P 支画笔替换为颜色 C o l Col Col

#include<stdio.h>
#include<math.h>
#include<algorithm>

using namespace std;
const int N = 1e6 + 10;
struct node{
	int l,r;
	int time,id;
}pen[4 * N];
struct Color{
	int p,c;
	int time;
}color[N];
int pos[N],a[N],res[N],cnt[N];
int n,m;

bool cmp(node a1,node a2) {
	if(pos[a1.l] != pos[a2.l]) return pos[a1.l] < pos[a2.l];
	else if(pos[a1.r] != pos[a2.r]) return pos[a1.r] < pos[a2.r];
	else return a1.time < a2.time;
}

void work() {
	int l = 0, r = 0,now = 0,time = 0;
	
	for(int i=1;i<=m;i++) {
		int ql = pen[i].l;
		int qr = pen[i].r;
		int qt = pen[i].time;
		
		while(l < ql) {
			cnt[a[l]] --;
			if(cnt[a[l]] == 0) now --;
			l ++;
		}
		
		while(l > ql) {
			l --;
			cnt[a[l]] ++;
			if(cnt[a[l]] == 1) now ++;
		}
		
		while(r < qr) {
			r ++;
			cnt[a[r]] ++;
			if(cnt[a[r]] == 1) now ++;
		}
		
		while(r > qr) {
			cnt[a[r]] --;
			if(cnt[a[r]] == 0) now --;
			r --;
		}
		
		while(time < qt) {
			time ++;
			int p = color[time].p;
			int c = color[time].c;
			if(p >= l && p <= r) {
				cnt[a[p]] --;
				if(cnt[a[p]] == 0) now --;
				
				cnt[c] ++;
				if(cnt[c] == 1) now ++;
				
			}
			
			swap(a[p],color[time].c);
		}
		
		while(time > qt) {
			int p = color[time].p;
			int c = color[time].c;
			if(p >= l && p <= r) {
				cnt[a[p]] --;
				if(cnt[a[p]] == 0) now --;
				
				cnt[c] ++;
				if(cnt[c] == 1) now ++;
			}
			
			swap(a[p],color[time].c);
			time --;
		}
		
		res[pen[i].id] = now;
	}
}

int main(){
	scanf("%d %d",&n,&m);
	
	int t = pow(n,2.0/3.0);
	
	int size = ceil((double)n / t);
	
	for(int i=1;i<=size;i++) {
		for(int j=(i-1)*t+1;j<=min(i*t,n);j++) {
			pos[j] = i;
		}
	}
	
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	
	int L = 0,k = 0;
	
	for(int i=1;i<=m;i++) {
		char s[2] = {0};
		scanf("%s",s);
		
		if(s[0] == 'Q') {
			k ++;
			scanf("%d %d",&pen[k].l,&pen[k].r);
			pen[k].time = L;
			pen[k].id = k;
		}
		else {
			L ++;
			scanf("%d %d",&color[L].p,&color[L].c);
			color[L].time = L;
		}
	}
	
	sort(pen+1,pen+1+k,cmp);
	m = k;
	
	work();
	
	for(int i=1;i<=m;i++) printf("%d\n",res[i]);
	
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值