题目链接:https://syzoj.com/problem/247
解题心得:
- 可持久化线段树其实就是一个线段树功能的加强版,加强在哪里呢?那就是如果一颗普通的线段树多次修改之后还能知道最开始的线段树长什么样子吗?肯定不能,如果就要问你这棵线段树在某一时刻是什么样子那能咋办。最直接的思维就是创建n棵线段树,将每一个时刻都记录下来。但是想一下时间复杂度和空间复杂度都是很大的。
- 可持久化线段树就是解决这个问题的。仔细想一想如果只修改线段树上一个叶子结点,那么对于线段树来说有改变的节点有多少个,log n 个,也就是线段树上面的一条枝。这样就可以建立一个新的根结点将需要改变的这个枝创建出来连接到新的结点上。那没有改变的节点咋办,那就值将这个新创建的节点指向上一个时刻树上没有改变的节点就行了。
// // ┏┛ ┻━━━━━┛ ┻┓ // ┃ ┃ // ┃ ━ ┃ // ┃ ┳┛ ┗┳ ┃ // ┃ ┃ // ┃ ┻ ┃ // ┃ ┃ // ┗━┓ ┏━━━┛ // ┃ ┃ 神兽保佑 // ┃ ┃ 代码无BUG! // ┃ ┗━━━━━━━━━┓ // ┃ ┣┓ // ┃ ┏┛ // ┗━┓ ┓ ┏━━━┳ ┓ ┏━┛ // ┃ ┫ ┫ ┃ ┫ ┫ // ┗━┻━┛ ┗━┻━┛ #include <bits/stdc++.h> using namespace std; const int maxn = 1e7+7; int lson[maxn], rson[maxn], T[maxn]; int n,m,cnt = 0, sz = 0, node[maxn]; void updata(int root) { node[root] = max(node[lson[root]], node[rson[root]]); } void build_tree(int &root, int l, int r){//注意要取地质,因为是创立的新的结点 root = ++sz; if(l == r) { scanf("%d", &node[root]); return ; } int mid = (l + r) >> 1; build_tree(lson[root], l, mid); build_tree(rson[root], mid+1, r); updata(root); } void init() { scanf("%d%d",&n,&m); build_tree(T[++cnt], 1, n); } void change(int& root, int pre, int pos, int l, int r, int va){ root = ++sz;//需要改变的结点 node[root] = node[pre]; if(l == r) { node[root] = va; return ; } lson[root] = lson[pre];//新的结点的儿子直接指向前一刻树的结点 rson[root] = rson[pre]; int mid = (l + r) >> 1; if(mid >= pos) change(lson[root], lson[pre], pos, l, mid, va); else change(rson[root], rson[pre], pos, mid+1, r, va); updata(root); } int query(int root, int ql, int qr, int l, int r) { if(l == ql && r == qr) { return node[root]; } int mid = (l + r) >> 1; if(mid >= qr) return query(lson[root], ql, qr, l, mid); else if(mid < ql) return query(rson[root], ql, qr, mid+1, r); else return max(query(lson[root], ql, mid, l, mid) , query(rson[root], mid+1, qr, mid+1, r)); } int main() { init(); int a,b,c,d; while(m--) { scanf("%d%d%d%d",&a,&b,&c,&d); if(a == 1) { change(T[++cnt], T[b], c, 1, n, d);//T记录第cnt课树是从哪里开始的 } else { int ans = query(T[b], c, d, 1, n); printf("%d\n",ans); } } return 0; }