高贵冷艳的主席树……
主席树是什么
是一种数据结构
主席树对于序列$1..i$的每一个前缀各建一颗值域线段树。
很重要的是主席树具有可减性,这是它能够区间操作的重要前提,这有点类似于前缀和的思想。
好的假设我们现在建出了这$n$颗线段树,那么我们的确是可以进行各种区间操作了。但是每颗线段树的空间是$maxn<<4$啊,空间复杂度显然要出事。
回到最初主席树的实现上来:1.它对于前缀建树;2.它建的是值域线段树。
性质一:对于前缀建树
那么相邻之间的两颗树的重复信息是很多的嘛,每次修改$logn$的节点信息,于是我们可以共享其余相同的节点。
性质二:建的是值域线段树
显然值域线段树的构造形态是和所处位置无关的,换句话说就是这$n$颗值域线段树的构造是相同的,只是其节点的$val$不一样。这一点保证了主席树共享节点后的空间复杂度的正确性。
至于如何共享节点,其实也没有什么刁钻的操作。我们只要动态开点,并且继承上一次的节点信息就好了。
以上是静态主席树的大致内容,挂一篇好博客:题解 P3834 【【模板】可持久化线段树 1(主席树)】
主席树的难点实际在于应用而不是其理解(怎么和网络流一样)。
例题
P3834 【模板】可持久化线段树 1(主席树)
题目描述
如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值。
输入输出格式
输入格式:
第一行包含两个正整数N、M,分别表示序列的长度和查询的个数。
第二行包含N个正整数,表示这个序列各项的数字。
接下来M行每行包含三个整数 l, r, kl,r,k , 表示查询区间 [l, r][l,r] 内的第k小值。
输出格式:
输出包含k行,每行1个正整数,依次表示每一次查询的结果
题目分析
挂代码:
1 #include<bits/stdc++.h> 2 const int maxn = 200035; 3 4 struct node 5 { 6 int l,r,val,root; 7 }a[maxn<<5]; 8 int tot,n,m,q; 9 int x[maxn],b[maxn]; 10 11 int read() 12 { 13 char ch = getchar(); 14 int num = 0; 15 bool fl = 0; 16 for (; !isdigit(ch); ch = getchar()) 17 if (ch=='-') fl = 1; 18 for (; isdigit(ch); ch = getchar()) 19 num = (num<<1)+(num<<3)+ch-48; 20 if (fl) num = -num; 21 return num; 22 } 23 int build(int l, int r) 24 { 25 if (l==r) return ++tot; 26 int rt = ++tot, mid = (l+r)>>1; 27 a[rt].l = build(l, mid); 28 a[rt].r = build(mid+1, r); 29 return rt; 30 } 31 int update(int pre, int l, int r, int x) 32 { 33 int rt = ++tot, mid = (l+r)>>1; 34 a[rt] = a[pre]; 35 a[rt].val++; 36 if (l < r){ 37 if (x <= mid) a[rt].l = update(a[pre].l, l, mid, x); 38 else a[rt].r = update(a[pre].r, mid+1, r, x); 39 } 40 return rt; 41 } 42 int query(int L, int R, int l, int r, int k) 43 { 44 if (l==r) return l; 45 int val = a[a[R].l].val-a[a[L].l].val, mid = (l+r)>>1; 46 if (k <= val) return query(a[L].l, a[R].l, l, mid, k); 47 return query(a[L].r, a[R].r, mid+1, r, k-val); 48 } 49 int main() 50 { 51 n = read(), q = read(); 52 for (int i=1; i<=n; i++) x[i] = b[i] = read(); 53 std::sort(b+1, b+n+1); 54 m = std::unique(b+1, b+n+1)-b-1; 55 a[0].root = build(1, m); 56 for (int i=1; i<=n; i++) 57 { 58 int tt = std::lower_bound(b+1, b+m+1, x[i])-b; 59 a[i].root = update(a[i-1].root, 1, m, tt); 60 } 61 for (int i=1; i<=q; i++) 62 { 63 int l = read(), r = read(), k = read(); 64 printf("%d\n",b[query(a[l-1].root, a[r].root, 1, m, k)]); 65 } 66 return 0; 67 }
【在更】