主席树入门+博客推荐

本文深入探讨了主席树的概念及其与线段树的关系,通过对比解释了主席树的结构与特性,提供了POJ2104 K-thNumber题目的代码示例,并推荐了多个学习资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

主席树入门+博客推荐

学习总结(可以直接翻到最下面,笔芯推荐的资料学习)

  • 主席树是就是多颗线段树的总结
  • 主席树的结构体中的 \(l\)\(r\) 代表的是这个节点的左右子节点的标号,因为空间优化的原因,他们可能不再符合左子树编号等于\(rt<<1\),右子树编号等于\(rt<<1|1\),这也是我开始比较困惑的一点。在学习主席树之前,需要你很熟悉线段树这个东西,因为主席树的主体是多颗线段树,首先我们来简单的回顾一下线段树的简单特点和性质,我们熟悉的线段树一般是用一个结构体表示一个节点,每个节点有一个编号,节点里面一般有两个变量l, r来表示这个节点维护的区间,然后还有一个区间信息(比如区间最大值,最小值,和等,视具体问题而定),当然如果涉及到区间更新,还有一个add或者lazy叫做延迟标记的东西,然后一般线段树最明显的特点就行,一个父节点的编号是i,那么他的两只儿子节点的编号分别为2 * i(左) , 2 * i + 1(右),注意主席树在这一点有别于一般的线段树,每一个父节点,他的两个儿子节点的编号不一定满足这个关系。
  • 主席树的结构体数组需要开多大呢?根据主席树的空间复杂度(这个我不会,我看的博客上的)是\(n*logn\),看的博客上右开 \(n\) 的 20倍和40倍的,这里还是最好算一下,开40倍就很大了。

这里附上一个简单题目的代码

POJ 2104 K-th Number,我也写了关于这个题的博客,代码有注释,可以方便理解。

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using  namespace std;
const int maxn=1e5+7;
int root[maxn], a[maxn], x, y, k;
int n, m, cnt, tot;
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)>>1;
    if(pos<=mid)
        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)>>1;
    int sum=t[t[y].l].sum - t[t[x].l].sum; 
    if(k<=sum)
        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() );
    tot=v.size(); //这里是记录实际需要记录的点的个数
    for(int i=1; i<=n; i++)
    {
        update(1, tot, 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, tot, root[x-1], root[y], k) - 1]);
    }
    return 0;
 } 

博客推荐

强烈推荐这个up主的讲解视频,很清晰,回答了很多问题,强烈推荐不过up主没有注意到一个问题,就是查找的范围不再是1到n了,因为离散化之后范围缩小了,应该是离散化之后的个数, 这里很重要。

主席树入门,讲解的比较细致,和线段树进行了对比,不错哦

有图,并且图没错还清晰

也有图,但是没代码,算是上面博客的补充把

这个号称最详细的,确实详细,不过一看这么长,我就没耐性看下去了,有耐心可以看看,毕竟代码有详细注释

转载于:https://www.cnblogs.com/alking1001/p/11422868.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值