正题
首先讲了很多东西:主席树,区间第K大,区间第K大EXT,块状链表,可持久化块状链表,区间第 K 大问题 EXTEXT,树上路径第K大问题......
但是这些东西太难了,所以我们来讲一讲可追溯化数据结构。
定义:现在有一个数据结构,支持一些修改操作和询问操作 我们在进行一些修改之后,发现某一次修改操作输入是错的,希 望修改这个修改操作,然后继续(哇真恶心,你修改的时候注意一点不就可以了
更严谨的定义:对于一个数据结构,维护一个操作序列,支持在某个位置插入一 个操作或者删除一个操作 对按照顺序执行这些操作后的状态进行一些询问 实现这些功能就叫部分可追溯化 如果还支持对前缀进行询问,就是完全可追溯化
可追溯化并查集
Normal:维护一个并差集的操作序列,支持以下操作: Insert(t,union(x,y)) 在第 t 次操作后插入合并 xy 所属的集合 询问当前的时刻 x,y 是否属于同一个集合
解法:非常简单,注意到 union 操作具有交换律,因此直接上并查集就行。
Completed:Insert(t,union(x,y)) 在第 t 次操作后插入合并 xy 所属的集合 询问在某次操作之后的一个时刻,x,y 是否属于同一个集合 比较简单。
解法:维护以 union 两个节点作为边,时刻作为边权的最小生成树。具体做饭LCT(不会
可追溯化队列
维护一个队列的操作序列,支持以下操作:
- Insert(t, enqueue(x)) 在第 t 次操作后插入将元素 x 入队操作。
- Insert(t, dequeue()) 在第 t 次操作后插入出队操作。
- Delete(t) 删除第 t 次操作。
Normal:回答询问:
- Query(front()) 询问当前时刻队列的下一个将被弹出的元素。
- Query(back()) 询问当前时刻队列中最后一个被加入的元素。
很容易可以发现其实先入队和先出队没有影响,只要保证里面的元素够弹出的就行了。
所以就直接用链表维护插入操作的时间序列,同时维护两个指针:首指针和尾指针。
若每次要插入一个入队操作,那么暴力插入。如果在首指针前面的话,那么就将首指针向左移动一位(删除同理
若每次要插入一个出队操作,那么直接将首指针向后移一位即可。(删除同理
Completed:回答询问:
- Query(t, front()) 询问 t 时刻后队列的下一个将被弹出的元素。
- Query(t, back()) 询问 t 时刻后队列中最后一个被加入的元素。
直接搞两棵以时间为关键字的主席树,分别维护入队操作和出队操作。
插入删除就直接基操。
先看看t之前有多少个出队操作,然后在第一棵平衡树上二分就可以了,好像比Normal的简单很多。
可追溯化栈
Completed:维护一个栈的操作序列,支持以下操作:
- Insert(t, push(x)) 在第 t 次操作后插入将元素 x 入栈操作。
- Insert(t, pop()) 在第 t 次操作后插入出栈操作。
- Delete(t) 删除第 t 次操作。支持以下询问:
- Query(t, top()) 询问 t 时刻后的栈顶元素。
因为部分可追溯化和完全可追溯化的操作其实差不多,而且复杂度甚至差不多,所以就不分开讲了。
考虑一个可能成为Top的元素,当且仅当后面的push可以被pop抵消掉,而且当前元素不会被中途pop掉。
前面这个东西很好理解:push=pop。后面这个东西怎么判定呢?后面不存在满足条件1的点。
怎么理解?可以把push操作看成1,把pop操作看成-1。做一遍后缀和,Top一定是右起第一个1。
假设存在两个1并且前面这个1是Top,因为后缀和相等,所以中间的push和pop操作数量是相同的,也就是说会把第一个1弹掉再加入第2个1,矛盾。所以判定条件如上。
所以我们要求的就是右起第一个1.
还要满足那么多操作。
平衡树:前面三个东西都是基操。
对于第四个操作,我们维护当前节点所代表的子树中的后缀最小值和最大值还有权值和,如果包含1,那么就肯定存在1这个后缀,后缀变化不超过1。平衡树上二分即可。
本文深入探讨可追溯化数据结构,包括并查集、队列与栈的可追溯化实现,及其在操作序列上的应用,如插入、删除、查询历史状态等,适合对高级数据结构有兴趣的读者。
934

被折叠的 条评论
为什么被折叠?



