浅谈莫队
普通莫队
不讲了
注意块大小最好调整为
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一下那个点当前的值和修改的值,这样就可以支持撤销了(时间轴移动回去的时候就相当于改回原来的值)
莫队+分块(根号分治?)
首先
[
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;
}
这样就能保证时间复杂度是一个根号的,自己想想都知道