poj2104(函数式线段树/主席树)

来源:互联网 发布:淘宝店铺装修 知乎 编辑:程序博客网 时间:2024/05/16 16:55

函数式线段树又称持久化线段树又称主席树

可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本、同时充分利用它们之间的共同数据减少时间和空间消耗。因此可持久化线段树也叫函数式线段树又叫主席树。


可持久化线段树

令 T 表示一个结点,它的左儿子是 left(T),右儿子是 right(T)。

若 T 的范围是 [L,R],那么 left(T) 的范围是 [L,mid],right(T) 的范围是 [mid+1,R]。

求区间第K小值问题

有n个数,多次询问一个区间[L,R]中第k小的值是多少。

查询[1,n]中的第K小值

我们先对数据进行离散化,然后按值域建立线段树,线段树中维护某个值域中的元素个数。在线段树的每个结点上用cnt记录这一个值域中的元素

数。那么要寻找第K小值,从根结点开始处理,若左儿子中表示的元素个数大于等于K,那么我们递归的处理左儿子寻找左儿子中第K小的数;若左儿

中的元素个数小于K,那么第K小的数在右儿子中,我们寻找右儿子中第K-(左儿子中的元素数)小的数。

查询区间[L,R]中的第K小值

我们按照从1到n的顺序依次将数据插入可持久化的线段树中,将会得到n+1个版本的线段树(包括初始化的版本),将其编0~n。可以发现所有版本

的线段树都拥有相同的结构,它们同一个位置上的结点的含义都相同。考虑第i个版本的线段树的结点P,P中储存的值表示[1,i]这个区间中,P结点

值域中所含的元素个数;假设我们知道了[1,R]区间中P结点的值域中所含的元素个数,也知道[1,L-1]区间中P结点的值域中所包含的元素个数,显然用

第一个个数减去第二个个数,就可以得到[L,R]区间中的元素个数。因此我们对于一个查询[L,R],同步考虑两个根root[L-1]与root[R],用它们同一个

位置的结点的差值就表示了区间[L,R]中的元素个数,利用这个性质,从两个根节点,向左右儿子中递归的查找第K小数即可。 

在推荐个学习链接:http://www.cnblogs.com/zinthos/p/3899565.html


然后再谈一下我对主席树的认识。个人感觉主席树就是n颗线段树的集合,但是这n颗线段树中有很多信息是一样的,所以可以共享

这些信息,每次建一颗新的线段树最多还要更新n个结点,所以每次更新只要加上n个结点就可以了。

时间复杂度:O(m*logn + n*logn)                                         空间复杂度:O(n*logn + 4*n)

poj2014是主席树的入门题

代码入下:

<span style="font-family:SimHei;font-size:14px;">#include<iostream>#include<algorithm>#include<cstring>#include<string>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdlib.h>#include<math.h>#define N 100005#define inf 0x7ffffff#define eps 1e-9#define pi acos(-1.0)using namespace std;struct Node{    int l,r;    int num;}tree[N*20];struct Node1{    int id,num;}s[N];int b[N];bool cmp(Node1 a, Node1 b){    if(a.num != b.num)        return a.num < b.num;    return a.id < b.id;}bool cmp1(Node1 a,Node1 b){    return a.id < b.id;}int rt[N];int cur;void PushUp(int k){    tree[k].num =  tree[tree[k].l].num + tree[tree[k].r].num;}int build(int l,int r){    int k = cur++;    if(l == r)    {        tree[k].num = 0;        return k;    }    int m = (l+r)/2;    tree[k].l = build(l,m);    tree[k].r = build(m+1,r);    PushUp(k);    return k;}int update(int o,int l,int r,int pos,int val){    int k = cur++;    tree[k] = tree[o];    if(l == r)    {        tree[k].num += val;        return k;    }    int m = (l+r)/2;    if(pos <= m) tree[k].l = update(tree[o].l,l,m,pos,val);    else tree[k].r = update(tree[o].r,m+1,r,pos,val);    PushUp(k);    return k;}int query(int l,int r,int ox,int oy,int k){    if(l == r)    {        return l;    }    int res = tree[tree[oy].l].num - tree[tree[ox].l].num;//pay attention to it    int m = (l+r)/2;    if(k <= res) query(l,m,tree[ox].l,tree[oy].l,k);    else query(m+1,r,tree[ox].r,tree[oy].r,k-res);}int main(){//freopen("input.txt","r",stdin);//freopen("output.txt","w",stdout);    int n,m;    while(scanf("%d%d",&n,&m) != EOF)    {        int i;        for(i = 1; i <= n; i++){            scanf("%d",&s[i].num);            s[i].id = i;        }        sort(s+1,s+n+1,cmp);//离散化        for(i = 1; i <= n; i++)            b[s[i].id] = i;        cur = 0;        rt[0] = build(1,n);        for(i = 1; i <= n; i++)            rt[i] = update(rt[i-1],1,n,b[i],1);        while(m--)        {            int x,y,k;            scanf("%d%d%d",&x,&y,&k);            int idx = query(1,n,rt[x-1],rt[y],k);            //cout<<idx<<endl;            printf("%d\n",s[idx].num);        }    }    return 0;}</span>


0 0
原创粉丝点击