【可持久化线段树】

版权声明:2018/4/10重启blog;转载请注明出处 https://blog.csdn.net/zhaiqiming2010/article/details/82055041

线段树

  线段树是一种特殊的BST,用节点代表区间,能够在logN时间内解决许多区间相关的问题,如区间最值,区间和,区间修改等。

可持久化

  可持久化让我们能够查询数据结构历史版本,并且通过利用之前的历史版本节省空间开销。

可持久化线段树

  线段树的每次修改都会至多修改logN个节点,而对于其余的节点并不会产生影响。每次修改的时候只添加logN个节点就成功的保存了历史版本,查询历史版本的时候从目标版本的根节点开始查询就可以了。

  ,

   这样整体的空间复杂度变成了(N<<2)+Q*(logN),各种操作的时间复杂度仍为LogN

  

扫描二维码关注公众号,回复: 2941356 查看本文章

  POJ2104 区间第K大模板题

  每次查询在L,R内第K大的数字,利用可持久化权值线段树,可以在LogN内完成每次的查询

  首先将原序列离散化之后按照权值线段树的规则进行可持久化的插入,具体就是先找出每个数字在整个序列中的名次(排第几),然后依次插入线段树,插入的操作是可持久化操作。这样我们就建树完成,(L-1,R]区间就代表了原序列第L到第R个数字的权值线段树。

  查询第K大的过程,设a[i]代表i的出现次数的话,如果a[1]+a[2]+a[3]=5;那么我们可以得出第5大的一定是3,这是权值线段树的好处,所以查询过程中直接查询从L到那个位置SUM为K就可以得出第K大的数的编号,然后在V数组映射得出,结束。 

#include<iostream>
#include<vector>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=1e5+6;
int n,m,cnt=0,root[maxn],a[maxn],x,y,k;
struct node{
    int l,r,sum;
}T[maxn*40];
vector<int>v;
int getid(int x){/
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void update(int l,int r,int &x,int y,int pos){
    T[++cnt]=T[y],T[cnt].sum++,x=cnt;
    if(l==r) return ;
    int mid=(l+r)/2;
    if(mid>=pos) update(l,mid,T[x].l,T[y].l,pos);
    else update(mid+1,r,T[x].r,T[y].r,pos);
}
 
int query(int l,int r,int x,int y,int k){
    if(l==r) return l;
    int mid=(l+r)/2;
    int sum=T[T[y].l].sum-T[T[x].l].sum;
    if(sum>=k) return query(l,mid,T[x].l,T[y].l,k);
    else return query(mid+1,r,T[x].r,T[y].r,k-sum);
}
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),v.push_back(a[i]);
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());
    for(int i=1;i<=n;i++) update(1,n,root[i],root[i-1],getid(a[i]));
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&k);
        printf("%d\n",v[query(1,n,root[x-1],root[y],k)-1]);
    }
}

猜你喜欢

转载自blog.csdn.net/zhaiqiming2010/article/details/82055041