主席树
Posted zhy-ak
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了主席树相关的知识,希望对你有一定的参考价值。
日常吐槽:好吧其实这个模板我就花了四个晚上调试,最后对着标一个一个看才改好,所以代码实现能力弱是个大坑:)
思路
主席树的另一个名字叫做可持久化权值线段树,用于维护多个版本的线段树。由于开多颗线段树的话空间会炸到飞起,所以可以充分利用每棵线段树中的重复部分。比如在第i + 1的版本的末尾新增一个节点,第i + 1棵版本的线段树和第i棵线段树的区别只是rt的右儿子及其子树中的一小部分。我们就可以在每一次更新版本时,另开那些有变化的节点,并与没有变化的节点连边(我们把这称之为动态开点)。这样即可节省大量空间,查询仿照普通线段树即可,
注意事项
1、有些题目一开始一定要开一棵空树;
2、要把每一个版本的根节点开个数组存起来;
例题及代码
JZOJ 3794 高级打字机
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int N = 10000007; int n, tot = 0, cnt = 0, t; int rt[N], lson[N], rson[N], len[N]; char v[N], c; void add(int &cur, int po, int l, int r, int last) { if (!cur) cur = ++tot; if (l == r) { v[cur] = c; return; } int mid = (l + r) >> 1; if (po <= mid) rson[cur] = rson[last], add(lson[cur], po, l, mid, lson[last]); if (mid + 1 <= po) lson[cur] = lson[last], add(rson[cur], po, mid + 1, r, rson[last]); } char ask(int po, int cur, int l, int r) { if (l == r) return v[cur]; int mid = (l + r) >> 1; if (po <= mid) return ask(po, lson[cur], l, mid); if (mid + 1 <= po) return ask(po, rson[cur], mid + 1, r); } int main() { scanf("%d", &n); memset(rt, 0, sizeof rt); memset(lson, 0, sizeof lson); memset(rson, 0, sizeof rson); memset(len, 0, sizeof len); for (int i = 1; i <= n; i++) { char type; scanf(" %c", &type); if (type == ‘T‘) { scanf(" %c", &c); len[++cnt] = len[cnt - 1] + 1; add(rt[cnt], len[cnt], 1, N, rt[cnt - 1]);//r在这道题中一定要是常数N哦,
不然不变化的节点所代表的区间就不一样了 } if (type == ‘U‘) { scanf("%d", &t); len[++cnt] = len[cnt - t - 1]; rt[cnt] = rt[cnt - t - 1]; } if (type == ‘Q‘) { scanf("%d", &t); printf("%c ", ask(t, rt[cnt], 1, N)); } } return 0; }
以上是关于主席树的主要内容,如果未能解决你的问题,请参考以下文章