浅谈莫队

普通莫队

不讲了
注意块大小最好调整为 n / m n/ \sqrt{m} n/m

带修莫队

相比起原来的莫队,加上了时间这一维,即变为 ( l , r , t ) (l, r, t) (l,r,t)
如果 l , r l, r l,r都在同一块,就按照 t t t从小到大排序,剩下的就和普通的莫队一样了
注意块大小是
在这里插入图片描述
时间复杂度是在这里插入图片描述

证明自己去找吧QWQ

实现上有个小细节,就是如果你修改移动时间轴的时候,修改的点如果在当前的 l , r l, r l,r中,可以直接 s w a p swap swap一下那个点当前的值和修改的值,这样就可以支持撤销了(时间轴移动回去的时候就相当于改回原来的值)

在这里插入图片描述

莫队+分块(根号分治?)

P4396 [AHOI2013]作业
在这里插入图片描述

首先 [ l , r ] [l, r] [l,r]这个可以直接拿一个莫队搞掉,但是对于 [ a , b ] [a,b] [a,b]这一维,如果拿树状数组维护前缀和会多一个log
考虑到移动是 n n n\sqrt{n} nn 级别的,但是询问个数只有 m m m个,所以可以拿一个修改 O ( 1 ) O(1) O(1),查询 O ( n ) O(\sqrt{n}) O(n )的数据结构分摊掉log,显然就是分块
移动的时候就在对应的位置和块上修改,查询的时候 O ( s q r t n ) O(sqrt{n}) O(sqrtn)
code

回滚莫队

AT1219 歴史の研究
当操作不支持撤回的时候(不具有可减性,如max),莫队就不能DEL()
那怎么办呢?
区间长度只能增长,不能缩短
考虑一个询问,如果在同一个块里,显然可以直接暴力
然后按左端点所属的块,一个个块考虑
对于当前块,强制 l l l在当前块的最右端,然后 r r r移动到 q r qr qr(递增的)的位置,移动到之后,把原来的答案记下来,然后 l l l暴力跳到 q l ql ql的位置,更新答案,然后再把答案等赋值回原来记着的状态(回滚)
具体看代码吧

void add(int id) {
	cnt[a[id]] ++;
	ans = max(ans, 1ll * b[a[id]] * cnt[a[id]]);
}
void roll(int id) {
	cnt[a[id]] --;
}
void solve(int id) {
	int i = 1;
	ans = 0;
	for(; i <= m && bel[q[i].l] < id;) i ++;
	int r = id * blo, l = r + 1;
	for(; i <= m && bel[q[i].l] == id; i ++) {
		if(bel[q[i].r] == id) { ANS[q[i].id] = calc(q[i].l, q[i].r); continue;}
		while(r < q[i].r) add(++ r);
		ll ls = ans;//移动左端点前记住答案
		while(l > q[i].l) add(-- l); 
		ANS[q[i].id] = ans; ans = ls;//赋值回原来的答案
		while(l <= id * blo) roll(l ++);//回滚
	}	
	for(int i = 0; i <= n; i ++) cnt[a[i]] = 0;
}

这样就能保证时间复杂度是一个根号的,自己想想都知道

posted @ 2021-03-28 16:43  lahlah  阅读(34)  评论(0编辑  收藏  举报