主席树(可持久化线段树)

狂风中的少年 提交于 2019-12-01 09:57:02

模板介绍

基础要求

  • 线段树

  • 能灵活运用 线段树

  • 前缀和,差分

概念

  • 可持久化:在某个历史版本上更改;查询某个历史版本上的值

引入

洛谷P3919 可持久化数组

这道可持久化线段树的题 大概就是单点修改&单点查询

代码大致就是在线段树的基础上改变她存点的方式

以前

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; }

真正的板子题

针对会主席树的同学

看看我写的思路 应该就可以秒懂了

P3834 可持久化线段树 1

建权值线段树

如图 每个点的权值为\([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; }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!