模板介绍
基础要求
线段树
能灵活运用 线段树
前缀和,差分
概念
- 可持久化:在某个历史版本上更改;查询某个历史版本上的值
引入
这道可持久化线段树的题 大概就是单点修改&单点查询
代码大致就是在线段树的基础上改变她存点的方式
以前
tree[k].l == k << 1 tree[k].r == k << 1 | 1
对于可持久化线段树
tree[k].l != k << 1 tree[k].r != k << 1 | 1
inline int tree_build(int k,int l,int r) { k = ++cnt; 每次这样来存点 但l和r仍然保留 表示一个递归和原序列位置的过程 if(l == r) { tree[k].val = seq[l]; return k; } int mid = l + r >> 1; tree[k].l = tree_build(tree[k].l,l,mid); tree[k].r = tree_build(tree[k].r,mid + 1,r); return k; }
大概酱紫
#include <map> #include <cstdio> #include <iostream> using namespace std; #define reg register int #define isdigit(x) ('0' <= (x)&&(x) <= '9') template<typename T> inline T Read(T Type) { T x = 0,f = 1; char a = getchar(); while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();} while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();} return x * f; } const int MAXN = 1e6 + 10; int seq[MAXN],root[MAXN],cnt,tot; struct node { int l,r,val; }tree[MAXN * 13]; inline int tree_build(int k,int l,int r) { k = ++cnt; if(l == r) { tree[k].val = seq[l]; return k; } int mid = l + r >> 1; tree[k].l = tree_build(tree[k].l,l,mid); tree[k].r = tree_build(tree[k].r,mid + 1,r); return k; } inline int update(int k,int l,int r,int pos,int v) { tree[++cnt] = tree[k]; k = cnt; if(l == r) { tree[k].val = v; return k; } int mid = l + r >> 1; if(pos <= mid) tree[k].l = update(tree[k].l,l,mid,pos,v); else tree[k].r = update(tree[k].r,mid + 1,r,pos,v); return k; } inline int query(int k,int pos,int l,int r) { if(l == r) return tree[k].val; int mid = l + r >> 1; if(pos <= mid) return query(tree[k].l,pos,l,mid); return query(tree[k].r,pos,mid + 1,r); } int main() { int n = Read(1),m = Read(1); for(reg i = 1;i <= n;i++) seq[i] = Read(1); root[tot] = tree_build(1,1,n); while(m--) { int v = Read(1),sit = Read(1),loc = Read(1); if(sit & 1) { int value = Read(1); root[++tot] = update(root[v],1,n,loc,value); } else { int ans = query(root[v],loc,1,n); root[++tot] = root[v]; printf("%d\n",ans); } } return 0; }
真正的板子题
针对会主席树的同学
看看我写的思路 应该就可以秒懂了
建权值线段树

如图 每个点的权值为\([a,b]\)的数值个数
样例输入
5 5 25957 6405 15770 26287 26465 2 2 1 3 4 1 4 5 1 1 2 2 4 4 1
数据很大 (−\(10^9\)≤\(a_i\)≤\(10^9\))想到离散化
int len = unique(seq + 1,seq + 1 + n) - seq - 1; for(reg i = 1;i <= n;i++) int it = lower_bound(seq + 1,seq + 1 + len,past_a[i]) - seq;
\(Firstly\)
查询思路
很容易理解 对于一个询问\([a,b]\)
我们先求\([1,b]\)中有多少个数值
再求\([1,a - 1]\)中的数值个数
每一个节点的权值对应相减
得到的就是\([a,b]\)中的数值个数
因为我们建立的是权值线段树
所以一个节点\(*p\)
如果\(k\)(第\(k\)小值)≤ \(*p->val\)
向左子树查找
反之向右
直到\(l == r\)
找到了第\(k\)小值\(hash\)(离散化)后的值
离散化时 用一个下标(离散化)后的值对应 原值的数组
就可以输出了
\(Secondly\)
查询要点
我们先求\([1,b]\)中有多少个数值
再求\([1,a - 1]\)中的数值个数
每一个节点的权值对应相减
这个操作明显复杂且难以实现 时间复杂度也不能保证
我们便考虑每次\(query\)时传两个(同时做两个线段树的操作)
inline int query(int a,int b,int k,int l,int r) { if(l == r) return l; int x = tree[tree[a].l].val - tree[tree[b].l].val; int mid = l + r >> 1; if(k <= x) return query(tree[a].l,tree[b].l,k,l,mid); return query(tree[a].r,tree[b].r,k - x,mid + 1,r); }
\(Thirdly\)
修改
最开始是空树
每次添点时把她当做一种新的历史状态
直接用主席树维护
\(Code\)
#include <map> #include <cstdio> #include <iostream> #include <algorithm> using namespace std; #define reg register int #define isdigit(x) ('0' <= (x)&&(x) <= '9') template<typename T> inline T Read(T Type) { T x = 0,f = 1; char a = getchar(); while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();} while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();} return x * f; } const int MAXN = 2e5 + 10; int seq[MAXN],root[MAXN << 5],past_a[MAXN],cnt,tot; struct node { int l,r,val; }tree[MAXN << 5]; inline int tree_build(int k,int l,int r) { k = ++cnt; if(l == r) return k; int mid = l + r >> 1; tree[k].l = tree_build(tree[k].l,l,mid); tree[k].r = tree_build(tree[k].r,mid + 1,r); return k; } inline int update(int k,int l,int r,int pos) { tree[++cnt] = tree[k]; k = cnt; tree[k].val++; if(l == r) return k; int mid = l + r >> 1; if(pos <= mid) tree[k].l = update(tree[k].l,l,mid,pos); else tree[k].r = update(tree[k].r,mid + 1,r,pos); return k; } inline int query(int a,int b,int k,int l,int r) { if(l == r) return l; int x = tree[tree[a].l].val - tree[tree[b].l].val; int mid = l + r >> 1; if(k <= x) return query(tree[a].l,tree[b].l,k,l,mid); return query(tree[a].r,tree[b].r,k - x,mid + 1,r); } int main() { int n = Read(1),m = Read(1); for(reg i = 1;i <= n;i++) past_a[i] = seq[i] = Read(1); sort(seq + 1,seq + 1 + n); int len = unique(seq + 1,seq + 1 + n) - seq - 1; root[tot] = tree_build(1,1,len); for(reg i = 1;i <= n;i++) { int it = lower_bound(seq + 1,seq + 1 + len,past_a[i]) - seq; root[i] = update(root[i - 1],1,len,it); } while(m--) { int l = Read(1),r = Read(1),k = Read(1); int ans = query(root[r],root[l - 1],k,1,len); printf("%d\n",seq[ans]); } return 0; }