2018-07-22 20:35:11
终于有了自己的主席书板子 先保存下来 明天写总结 挖坑++(坑已填)
这个是只有查询操作的,没有修改操作的,单点修改操作的挖坑加加
推荐一个博客,才学会了这一些,但是建树的过程自己还有一点疑惑,画不出来图:https://blog.csdn.net/creatorx/article/details/75446472
主席树主要可以用来解决区间第k小的问题,这个问题呢,在没有时间限制的条件下我们可以通过对每段区间进行排序然后输出就好,但是可以很明显的看到,时间复杂度爆棚,在ACM中不能这么玩,于是想一下线段树,在单独的一次询问中,我们使用线段树求解区间第k大的问题,首先将该范围内的元素建树,线段树的每个节点都有一个左右区间,每个节点中还记路这一些信息,这些信息可以使最大值,最小值,和等,在这里我们记录的是这个区间里面数字的个数,于是当我们求第k大的时候,如果左区间的元素个数大于k那么我们就去左区间寻找,如果左区间数字个数小于k,那么第k小一定出现在右区间中,于是我们就去右区间中寻找第k-d小,其中d为左区间数字个数。
在多次查询中,每次都这么建树,时空复杂度与直接排序差不多,甚至更高,于是我们就在想,能不能一次建立线段树之后不需要再次建树了,每次在该树上查询就好了,这里就是主席树的作用,主席树又称可持久化线段树,我们先来复习一下前缀和的概念,对于每次询问【l , r】区间内元素的和,我们只需要将sun[j]-sun[i]就好,能不能把这个运用到线段树上,对于每次询问区间【l , r】第k小,我们只需要将ans[r]-ans[l]即可。这样的话,就引出了线段树,对于
#include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; const int maxn = 1e5+10; int n , m; int cnt; struct node { int L , R; ///该节点的左右节点 int sum; ///该节点所管辖区域内数字的个数 node() { sum = 0; } } Tree[maxn*20]; struct value { int x; int id; } val[maxn]; ///离散化 bool cmp(value a, value b) { return a.x < b.x; } int root[maxn]; ///多颗线段树的根节点 int rank[maxn]; ///原数组离散之后的数组 void init() { cnt = 1; root[0] =0; Tree[0].L = Tree[0].R = Tree[0].sum = 0; ///初始化建立一个空节点 } void input() { for(int i=1; i<=n; i++) { scanf("%d" , &val[i].x); val[i].id = i; } } void update(int num , int &rt , int l , int r) { Tree[cnt++] = Tree[rt]; rt = cnt-1; Tree[rt].sum++; ///增加了一个数字 if(l == r) return; int mid = (l+r)/2; if(num <= mid) update(num , Tree[rt].L , l , mid); else update(num , Tree[rt].R , mid+1 , r); } int query(int i , int j , int k , int l , int r) { if(l == r) return l; int d = Tree[Tree[j].L].sum-Tree[Tree[i].L].sum; int mid = (l+r)/2; if(k <= d) return query(Tree[i].L , Tree[j].L , k , l , mid); else return query(Tree[i].R , Tree[j].R , k-d , mid+1 , r); } void solve() { int l , r , k; for(int i=1; i<=m; i++) { scanf("%d%d%d" , &l , &r , &k); printf("%d " , val[query(root[l-1] , root[r] , k , 1 , n)].x); } } int main() { while( scanf("%d%d" , &n , &m) != EOF ) { input(); sort(val+1 , val+1+n , cmp); for(int i=1; i<=n; i++) { rank[val[i].id] = i; } ///离散化结束 init(); for(int i=1; i<=n; i++) { root[i] = root[i-1]; update(rank[i] , root[i] , 1 , n); } solve(); } return 0; }