Skip to content

历史版本和 笔记

一、简要介绍

维护序列 A,支持下列几种操作:

  • 给出 l,r,x,将 Alr 加上 x
  • 记录一个版本,并复制一个新版本,以后的操作在新版本进行。
  • 给出 l,r,求 Alr 所有历史版本的和。

操作总数为 mn,m5×105

对于一个线段树节点,它的标记队列(就是将标记从前往后放入一个队列中)一定如下,其中 vi 对应一次区间加更新加的值memory 对应记录版本的操作。

{v1,v2,memory3,v4,v5,v6,memory7,}

而对应那个时候即将下传的 tag 就是 ti=i=1nvi

u 表示这个区间初始的和,shi 表示操作完前 i 个操作后的历史版本和。

shi=j=1i[qj=memory](u+tj)=u×(j=1i[qj=memory])+j=1i[qj=memory]ti

也就是说,记录 memory 出现的个数,记作 ci;以及 memory 时的 ti 的和即可,将这个和记作 si

每次更新时,先更新历史版本和,方法是让历史版本和加上未更新的和乘上 ci,再加上 si;当前和再更新(len 是当前区间长度,其他的变量而不是结构体成员的,都是来自父亲的信息)。

cpp
t[rt].sh += tc * t[rt].s + ts;
t[rt].s += tag * len;

接着考虑自己的 tag c1,s1 怎么接受来自父亲的 tag c2,s2,考虑接受父亲的 tag 后,新的历史版本和 sh

sh=u×c+s=u(c1+c2)+s

根据线段树 tag 的定义,父亲的 tag 一定是比自己的 tag 的“更新时间”要晚,故 s2 代表的标记队列是接在 s1 以后的,所以 s2 中的 c2ti 都应该加上 s1

综上:

cc1+c2ss1+s2+c2s1

(变量而不是结构体成员的,都是来自父亲的信息)。

cpp
t[rt].tc += tc;
t[rt].ts += tc * t[rt].tag + ts;
t[rt].tag += tag;

二、刷题总结

1. P3246 [HNOI2016] 序列

解法 (1)

离线询问,对 r 进行扫描线,线段树维护以 r 为右端点时,每个左端点的最小值。

意思就是线段树的区间 [l,r][1,r],维护的是 [[l,r],r] 的最小值的相关信息。

假设扫描线扫到 r,那么新进入一个 ar,就是 [[1,r],r] 的最小值对 armin,一个标准的 beats 操作。

当扫描到 r 时,查询区间 [l,r] 所有连续子序列的答案,先考虑固定左端点 l 的情况,那么就是 i=lrminj=liaj(也就是扫描到 i 时,线段树维护的 l 这一点的最小值)。

如果把扫描到的不同的 r 看作若干个版本,固定左端点 l 的子序列的答案,就相当于 l 这一点的最小值的历史版本和。

那么不固定左端点 l,就是对 [[l,r],r] 进行一次上面的操作,也就是求 [l,r] 区间的最小值历史版本和就好了。

解法 (2)

由 NOIP 2024 T4,ar 能成为 max 的区间,本质上就是往前找第一个比 ar 大的数 al[[l+1,r1],r1] 的最大值一定是 al,那么 [[l+1,r],r] 的最大值就变成 ar,使用一个单调栈,对区间进行加减即可解决,所以并不用 beats。

2. CF997E Good Subsegments

好区间换句话就是说 (maxmin)(rl)=0 的区间,所以直接维护那个式子的最小值和出现次数。

同理,离线询问,对 r 进行扫描线,线段树也和上面的差不多,线段树里的 [l,r][1,r] 表示 [[l,r],r] 的式子最小值。把不同的 r 看成不同的版本。

r 向右移动时增加了 1,故先给这一整个式子减小 1。然后用两个单调栈维护当前 ai 能够成为 max/min 的区间,对这些区间的式子值进行修改。

然后同上题,求答案即可,答案就是 [l,r] 内每一点最小值的出现个数的历史版本和。

为什么最小值就行了?易得 [i,i] 时式子最小值是 0

这题还有个牛逼的地方,很多题解都没讲:他没有加法标记。因为最小值个数由 push_up 决定,所以相当于它的标记队列全是 memory,所以不用维护 si……

3. P10637 BZOJ4262 Sum

有一个经典的 max/min 的转换:minai=maxai,那么题目要求的就变成了:

l[l1,r1]r[l2,r2](maxi[l,r]ai+maxi[l,r]ai)

解决一个就可以解决另一个。

依旧考虑对 r 进行扫描线,只需对 r 进行差分,将答案变为 [[l1,r1],r2] 的答案减去 [[l1,l2],l21] 的答案即可。那么就变成了第一题。

4. P8868 [NOIP2022] 比赛

神题。

题目要求:

p[l,r]q[l,r](maxi[p,q]{ai}×maxi[p,q]{bi})

相当于上一题从加号变成了乘号。

考虑只算区间乘积,怎么做(p,q 分别是 ai,bi 的加法 tag,l 是区间长度)。

(ai+p)(bi+q)=aibi+aiq+bip+pq=(aibi)+q(ai)+p(bi)+pql

将红色的部分当作 tag,做历史版本和即可。

最近更新