主席树学习笔记
简介:
主席树是一种可持久化数据结构,全称为可持久化权值线段树,支持查询历史版本内信息,单点修改同时新建版本等操作,也是一种函数式线段树, 可支持完全持久化。
原理:
首先考虑朴素的做法,每次新建版本时都 copy 一份,并且在这个 copy 下来的版本上面进行操作。这种做法空间复杂度是很大的。
观察到我们每次插入一个点后,至多有 \(O(\log n)\) 个节点的信息发生改变。
我们可以沿用动态开点的思想,每次修改时对于新版本的根节点以及从根节点到需要修改的节点的位置中的每一个点都新开一个节点,并同时将新节点的对应的旧节点的信息 copy 过来,并在这个基础上修改。
这样说有点抽象,可以根据下面的图理解。
(其中红色节点为新开的节点)
例题:【模板】可持久化线段树 2
考虑对于每个 \(a_i\) 在主席树上都建一个新的版本。
那么我们该怎么回答询问呢?
由于主席树关于函数的美妙性质,主席树版本之间是可减的。具体的,我们对于每个节点维护一个 \(sum\) 记录该节点对应的区间有多少个数。对于询问 \([l,r]\),(先咕了)
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 2e5 + 10; struct Persist_tree{ #define mid (l + r >> 1) struct node{ int sum, ls, rs; }hjt[N << 5]; int cnt, root[N]; void upd(int l, int r, int pre, int& now, int x){ hjt[++cnt] = hjt[pre]; now = cnt; hjt[now].sum++; if(l == r) return; if(x <= mid) upd(l, mid, hjt[pre].ls, hjt[now].ls, x); else upd(mid + 1, r, hjt[pre].rs, hjt[now].rs, x); } int querykth(int l, int r, int Lo, int Ro, int k){ if(l == r)return l; int rank = hjt[hjt[Ro].ls].sum - hjt[hjt[Lo].ls].sum; if(k <= rank) return querykth(l, mid, hjt[Lo].ls, hjt[Ro].ls, k); else return querykth(mid + 1, r, hjt[Lo].rs, hjt[Ro].rs, k - rank); } }tr; int n, m, a[N], rk[N]; int getid(int val){return lower_bound(rk + 1, rk + m + 1, val) - rk;} signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int T; cin >> n >> T; for(int i = 1; i <= n; i++)cin >> a[i], rk[i] = a[i]; sort(rk + 1, rk + n + 1); m = unique(rk + 1, rk + n + 1) - (rk + 1); for(int i = 1; i <= n; i++)tr.upd(1, m, tr.root[i - 1], tr.root[i], getid(a[i])); while(T--){ int l, r, k; cin >> l >> r >> k; cout << rk[tr.querykth(1, m, tr.root[l - 1], tr.root[r], k)] << "\n"; } return 0; }
本文作者:little-corn
本文链接:https://www.cnblogs.com/little-corn/p/18157517
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步