可持久化线段树
前置知识:线段树
可持久化线段树建立在线段树的基础上,通过只保存部分被影响的结点,从而保存历史版本线段树。
因此祖传线段树的类似堆一样的存储方式是不能动态存入新的点的,需要动态开点。
大致过程
对于可持久化线段树,需要的也就是:
-
动态开点
其实就是事先开个大数组,然后找个变量增加结点即可。
-
连接旧结点
可以发现每次修改影响的结点就那几个。所以,可以对这些受影响的结点另开个结点,然后连接(比如右儿子+1的话,那么新开个结点,记录右儿子+1的结果,同理这个结点的信息也需要被记录,不过左儿子没有改变,所可以让新的结点连接到旧的左儿子即可)。
-
记录历史版本
每个修改操作一定会使得增加一个新的根结点,事先开一个结点,记为新的根结点,并保存在历史版本里面即可。(有的题目要求包括了没有修改的操作)
例题
洛谷 P1383 高级打字机
是可持久化线段树的板子题。
题目大意
需要实现三个操作:type
、Undo
、query
。
- type:在字符串最后面添加字符。
- query:单点查询字符
- undo:撤回操作,包括undo和type
可以发现这个字符串的最大长度一定不超过操作总数,则可以先按操作数建线段树,之后再动态记录历史版本和字符串长度即可。
非叶子结点在这里只有记录目前字符串长度的作用。
struct Node {
char val;
int lc, rc;
int len;
};
constexpr int MAXN = 1e6 + 10;
Node tree[MAXN << 5];
int ver[MAXN];
int tot = 1;
void build(int pll, int prr, int pos) {
if (pll == prr) {
tree[pos].val = 0;
tree[pos].len = 0;
return;
}
int mid = pll + ((prr - pll) >> 1);
tree[pos