主席树

//SPOJ dquery 

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;


//主席树:区间不同数的个数 

/*
 * 给出一个序列,查询区间内有多少个不相同的数
 */


/*
主席树是利用历史信息建树的可持久化线段树 
线段树的每一个节点代表一个区间,主席树的每个节点也代表一个区间
主席树将从根节点(代表大区间[L,R])到叶子结点(代表点区间[x,x])的每个路径都建立一棵树
对于查询区间[l,r]不同数的个数,主席树从后到前也就是从R->L的顺序建立每个从[L,R]到[x,x]路径的树,每个节点代表该节点所代表的区间不同数的个数 ,注意因为是从后到前建立树,所以我们是没有考虑[x,x]点之前的数的影响,他们都是0 ,只有[l,l]点后面的数的路径的树才能对[l,l]路径的树产生影响 
如果要查询[l,r]区间的不同数的个数,那么我们就要在 这样的一棵树上查找,他是从[L,R]到[l,l]路径建立的树,他的节点代表该区间不同数的个数,只需要收集每个小区间的不同数的个数,我们就得到了答案

总之,主席树的建树是对区间内的每个数都建立了一棵树
如果要查找某个区间不同数的个数,我们就只需要在从[L,R]到[x,x]路径的这棵树上查找就可以了

为了节省区间,我们首先建立一棵空树,在空树的基础上不断添加到每个数的路径 
*/ 


const int MAXN = 30010;
const int M = MAXN * 100;
int n,q,tot;
int a[MAXN];
int T[M],lson[M],rson[M],c[M];


//建立空树 
int build(int l,int r){
    int root = tot++;
    c[root] = 0;
    if(l != r){
        int mid = (l+r)>>1;
        lson[root] = build(l,mid);
        rson[root] = build(mid+1,r);
    }
    return root;
}
//添加从[L,R]到[x,x](代表数a[x])的路径 
int update(int root,int pos,int val){
    //路径的根节点 
    int newroot = tot++, tmp = newroot;
    //存储不同数的个数,root是历史信息,存储前一棵树代表该区间的不同数的个数 
    c[newroot] = c[root] + val;
    //大区间 
    int l = 1, r = n;
    //[L,R]到[l,r]的路径 
    while(l < r){
        int mid = (l+r)>>1;
        if(pos <= mid){
            lson[newroot] = tot++; 
            rson[newroot] = rson[root];
            newroot = lson[newroot]; 
            root = lson[root];
            r = mid;
        }
        else{
            rson[newroot] = tot++; 
            lson[newroot] = lson[root];
            newroot = rson[newroot]; 
            root = rson[root];
            l = mid+1;
        }
        //新路径的节点的值是从上一个树得到的 
        c[newroot] = c[root] + val;
    }
    //新路径的树的根节点 
    return tmp;
}
int query(int root,int pos){
    int ret = 0;
    int l = 1, r = n;
    while(pos < r){
        int mid = (l+r)>>1;
        if(pos <= mid){
            r = mid;
            root = lson[root];
        }
        else{
            ret += c[lson[root]];
            root = rson[root];
            l = mid+1;
        }
    }
    return ret + c[root];
}



int main(){
    while(scanf("%d",&n) == 1){

        tot = 0;

        for(int i = 1;i <= n;i++)
            scanf("%d",&a[i]);
        //建立空树 
        T[n+1] = build(1,n);
        //存储每个数的位置,离开始建树的最近的位置 
        map<int,int>mp;
        for(int i = n;i>= 1;i--){
            //如果不在mp里面 
            if(mp.find(a[i]) == mp.end()){
                T[i] = update(T[i+1],i,1);
            }
            //已经存在过 
            else{
                //如果之前出现过,则将之前的路径的树的那个点消灭掉,以这个路径为基础,新建到[i,i]的路径的树 
                int tmp = update(T[i+1],mp[a[i]],-1);
                T[i] = update(tmp,i,1);
            }
            //最近的位置 
            mp[a[i]] = i;
        }

        scanf("%d",&q);
        while(q--){
            int l,r;
            scanf("%d%d",&l,&r);
            //查询[L,R]到[x,x]的路径的树 
            printf("%d\n",query(T[l],r));
        }
    }
    return 0;
}
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;


//主席树:静态区间第k大 

const int MAXN = 100010;
const int M = MAXN * 30;
int n,q,m,tot;
int a[MAXN], t[MAXN];
int T[M], lson[M], rson[M], c[M];

void dis(){
    for(int i = 0 ;i< tot ;i++){
        cout<<i<<" "<<a[i]<<" "<<t[i]<<" "<<T[i]<<" "<<lson[i]<<" "<<rson[i]<<" "<<c[i]<<endl;
    }
    cout<<endl;
} 


//得到不同数的个数m和获得某个数在序列中的排名 
void Init_hash(){
    for(int i = 1; i <= n;i++)
        t[i] = a[i];
    sort(t+1,t+1+n);
    m = unique(t+1,t+1+n)-t-1;
}
//获得排名 
int hash(int x){
    return lower_bound(t+1,t+1+m,x) - t;
}

//建立空树 
int build(int l,int r){
    int root = tot++;
    c[root] = 0;
    if(l != r){
        int mid = (l+r)>>1;
        lson[root] = build(l,mid);
        rson[root] = build(mid+1,r);
    }
    return root;
}
//添加路径 
int update(int root,int pos,int val){
    int newroot = tot++, tmp = newroot;
    c[newroot] = c[root] + val;
    int l = 1, r = m;
    while(l < r){
        int mid = (l+r)>>1;
        if(pos <= mid){
            lson[newroot] = tot++; 
            rson[newroot] = rson[root];
            newroot = lson[newroot]; 
            root = lson[root];
            r = mid;
        }
        else{
            rson[newroot] = tot++; 
            lson[newroot] = lson[root];
            newroot = rson[newroot]; 
            root = rson[root];
            l = mid+1;
        }
        c[newroot] = c[root] + val;
    }
    return tmp;
}
/*
主席树:静态区间第k大问题,对于第x个数所建立的路径的数,他代表的含义是[x...R]也就是x开始的后缀中不同数的个数,该路径的叶子结点[y,y](注意这个序列的第x个数的下标x和在主席树中的下标区间[y,y]是不一样的,因为x代表实在整个序列的第x个,而y代表第x个数在整个序列中是第y大的数),存储的数值代表后缀中第y大的数的个数
也就是说,主席树的叶子结点[y,y]存储的数值代表该路径的后缀中第y大(这个第y大表示的是整个序列而言)的数的个数,要获得区间[l,r]中第k大的数,就要在[l,l]路径所代表的树和[r+1,r+1]路径所代表的树里面查找,他们分别代表[l...R]和[r+1...R]后缀中不同数的个数和每个第某大的数的个数
*/ 
int query(int left_root,int right_root,int k){
    int l = 1, r = m;
    while( l < r){
        int mid = (l+r)>>1;
        //left_root 和 right_root里面不大于各自区间内最大值的个数  
        //lson[left_root] 区间中不大于该区间最大值的数的个数,c[lson[left_root]]
        //两个相减就代表对于左子区间中不大于该区间最大值的数的个数,如果k比这个数小,则代表所求区间的第k大在左区间,否则右区间
        //其实自己画一个图就好懂了,主席树建树过程都一样,不过不同问题查询的不一样 
        if(c[lson[left_root]]-c[lson[right_root]] >= k ){
            r = mid;
            left_root = lson[left_root];
            right_root = lson[right_root];
        }
        else {
            l = mid + 1;
            k -= c[lson[left_root]] - c[lson[right_root]];
            left_root = rson[left_root];
            right_root = rson[right_root];
        }
    }
    return l;
}
int main(){
    while(scanf("%d%d",&n,&q) == 2){
        tot = 0;
        for(int i = 1;i <= n;i++)
            scanf("%d",&a[i]);

        //得到不同数的个数和序列 
        Init_hash();

        //对m个不同的数建立空树,存储后缀中第某大的个数 
        T[n+1] = build(1,m);
        for(int i = n;i ;i--){
            //第i个数a[i]在序列中排第hash(a[i])名 
            int pos = hash(a[i]);
            //对pos所在的路径建树 
            T[i] = update(T[i+1],pos,1);
        }

        while(q--){
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",t[query(T[l],T[r+1],k)]);
        }
    }
    return 0;
}
待补
LCA + 主席树 = 树上路径点值第k大
树状数组 + 主席树 = 动态区间第k大
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值