k-th number(主席树 )

题意:给一堆数,找区间第k大的数。

主席树是指在一棵树的基础上复制一棵树,同时又在其中加入一个点。
可以用来查询第k大的点。
这个与直接建立多颗线段树还是有区别的。
这个复制可以节省很多内存~

代码如下:

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn=100010;
/*
线段树点记录的是数字出现的次数
*/
struct node{
    int L,R,sum;       
}tree[maxn*20];
struct node1{
    int x,id;       
    bool operator <(node1 y)const{//重载运算符,<时以x的大小来判断。sort时用到
        return x<y.x;
    }
}a[maxn];
int cnt,n,m;
int root[maxn],rank[maxn];
void create_tree(int num,int &x,int l,int r){//num表示离散化后的编号,x表示根节点,l,r不解释
    tree[cnt++]=tree[x],x=cnt-1;//结构体复制,将新建的树复制成上一棵树 
    /*在树上新增一个节点*/
    ++tree[x].sum;
    if(l==r) return;
    int mid=(l+r)>>1;
    /*建树的点的位置*/
    if(num<=mid) create_tree(num,tree[x].L,l,mid);//左边则向左子树找 
    else create_tree(num,tree[x].R,mid+1,r);//右边则向右子树 
}
int query(int i,int j,int z,int l,int r){
    if(l==r) return l;
    int t=tree[tree[j].L].sum-tree[tree[i].L].sum;
    //先搜索左子树,如果规定区间内的个数比要求的个数多,则在左子树内,反之成立
    int mid=(l+r)>>1;
    if(z<=t) return query(tree[i].L,tree[j].L,z,l,mid);
    else return query(tree[i].R,tree[j].R,z-t,mid+1,r);
}
int main(){
    int i,j;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++){
        scanf("%d",&a[i].x); 
        a[i].id=i;
    }
    sort(a+1,a+1+n);
    for(i=1;i<=n;i++) rank[a[i].id]=i;//离散化
    cnt=1;
    //for(i=1;i<=n;i++) printf("%d ",rank[i]);
    for(i=1;i<=n;i++){
        root[i]=root[i-1];//将根节点复制
        create_tree(rank[i],root[i],1,n);//建树 
    }
    for(i=1;i<=m;i++){
        int p,q,z;
        scanf("%d%d%d",&p,&q,&z);
        printf("%d\n",a[query(root[p-1],root[q],z,1,n)].x); //用根节点来表示树                
    }
    return 0;    
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值