主席树

54 篇文章 0 订阅
21 篇文章 0 订阅

作用

用于查询区间第k小值等,本质是线段树,因为是某个主席发明而得名,算法内容与主席无关.

实现方法

线段树维护序列的值的个数,并对每一个前缀建一棵线段树(主席树不是这样的,否则空间…),那么要查区间[l,r]就只需要将r这棵树减去(l-1)这棵线段树,在相减后的线段树查找即可(实际不需要再建一棵树).
空间优化:根据上述方法,可以发现相邻两棵线段树的区别只有log,因而后面的线段树可以与前面的线段树共用节点,总空间复杂度为O(n*log^2).

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#define N 200100
using namespace std;

int n,m,num[N],b[N],c[N],cc,tt,root[N];
struct Tree
{
    int l,r,gs,l_son,r_son;
}tree[N*50];
map<int,int>id;

void build(int now,int u,int v)
{
    tree[now].l=u;
    tree[now].r=v;
    tree[now].gs=0;
    if(u!=v)
    {
        int mid=(u+v)/2;
        tt++;
        tree[now].l_son=tt;
        build(tt,u,mid);
        tt++;
        tree[now].r_son=tt;
        build(tt,mid+1,v);
    }
    else
    {
        tree[now].l_son=tree[now].r_son=-1;
    }
}

void add(int now,int u)
{
    if(tree[now].l_son!=-1&&u<=tree[tree[now].l_son].r)
    {
        tt++;
        tree[tt]=tree[tree[now].l_son];
        tree[tt].gs++;
        tree[now].l_son=tt;
        add(tt,u);
        return;
    }
    else if(tree[now].r_son!=-1&&u>tree[tree[now].l_son].r)
    {
        tt++;
        tree[tt]=tree[tree[now].r_son];
        tree[tt].gs++;
        tree[now].r_son=tt;
        add(tt,u);
        return;
    }
    return;
}

int ask(int u,int v,int w)
{
    if(tree[u].l==tree[u].r)
        return c[tree[u].l];
    if(w<=tree[tree[u].l_son].gs-tree[tree[v].l_son].gs)
        return ask(tree[u].l_son,tree[v].l_son,w);
    return ask(tree[u].r_son,tree[v].r_son,w-(tree[tree[u].l_son].gs-tree[tree[v].l_son].gs));
}

int main()
{
    int i,j,o,p,q;
    cin>>n>>m;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
        b[i]=num[i];
    }
    sort(b+1,b+n+1);
    for(i=1;i<=n;i++)
    {
        if(b[i]!=c[cc]||!cc)
        {
            cc++;
            c[cc]=b[i];
            id[b[i]]=cc;
        }
    }
    root[0]=1;
    tt++;
    build(1,1,cc);
    for(i=1;i<=n;i++)
    {
        tt++;
        root[i]=tt;
        tree[root[i]]=tree[root[i-1]];
        tree[root[i]].gs++;
        add(root[i],id[num[i]]);
    }
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&p,&q,&o);
        printf("%d\n",ask(root[q],root[p-1],o));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值