首页 > 其他分享 >主席树的简要讲解

主席树的简要讲解

时间:2024-04-23 12:22:34浏览次数:11  
标签:简要 int 线段 tr ch 讲解 区间 主席 define

区间第k小值

主席树是解决动态查找序列上[l,r]区间中的第k小值的一个数据结构

核心思想:动态开点(后面会讲)
传统线段树都是值域线段树
其实意思就是每个节点都存的是序列上[l,r]的一个区间和,但是考虑我们需要动态处理区间的不是最值,故换一种线段树

主席树一般用的是权值线段树
就是把[l,r]改为[min(a),max(a)] a是序列A在[l,r]区间里的一个子序列,每个节点下都保存一个统计数字s,代表在当前权值域里,有多少个数字
array : 1 4 3 3

           [1,4]
           s = 4
    [1,2]         [3,4]
    s = 1         s = 3
[1,1]  [2,2]    [3,3] [4,4]
s = 1  s = 0    s = 2  s = 1

 

 (不会画图QWQ)

 步入正题
我们转换思想,每一次开点都存储一个版本的权值线段树,但是这样会让空间复杂度直线上升为O(n^2),这是不能容忍的
但是我们再想
这是一颗树性结构,我们每一次加点都只会改变上下log n个点的数据,为什么我们就不能每一次都开一个log n的新点,将与这次更改无关的点直接开一条新边连接到新点呢?

不能像上一个那样形似了,我直接盗图

这是第一个版本在insert(4)后的结果
每一次insert都存储一个root[i+1],表示第i+1个版本的根节点

 

但是如何查询[l,r]区间内的第k小数呢
这时候就要运用我们前缀和的思想


 [l,r]的信息 = [1,r]的信息 - [1,l-1]的信息
即 S[l,r] = S[1,r] - S[1,l-1]

离散化
这个其实没什么好讲的,就是预处理,详情见代码

 

大概意思是懂了吧,来,让我们步入总结

常规操作
1.插入insert(),动态开点
2.查询[l,r]区间内第k小值
3.离散化

时间复杂度 O(n log^2 n)

luogu P3834
#### C++ 代码

 1 #include"bits/stdc++.h"
 2 
 3 using namespace std;
 4 const int N=200010;
 5 #define inl inline
 6 #define reg register
 7 #define regi register int
 8 #define PII pair<int,int>
 9 //#define ll long long
10 inl int read(void)
11 {
12     int x=0,f=1;char ch=getchar();
13     while(!isdigit(ch))  f=ch=='-'?-1:f,ch=getchar();
14     while(isdigit(ch))   x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
15     return x*f;
16 }
17 int a[N],n,m;
18 vector<int>    num;
19 struct node
20 {
21     int l,r,s;//左右儿子下标,区间中一共有多少个数
22 }tr[N*20];
23 int root[N],idx;//root[i]为第i课线段树的节点编号
24 #define id(x)    lower_bound(num.begin(),num.end(),x)-num.begin()
25 //id(x)为映射
26 int build(int l,int r)
27 {
28     int p=++idx;
29     if(l==r)    return p;
30     int mid=l+r>>1;
31     tr[p].l=build(l,mid),tr[p].r=build(mid+1,r);
32     return p;//返回当前节点的编号
33 }
34 inl int insert(int p,int l,int r,int x)
35 {
36     int q=++idx;//创建新点q
37     tr[q]=tr[p];//复制旧点p 
38     if(l==r)
39     {
40         tr[q].s++;
41         return q;
42     }
43     int mid=l+r>>1;
44     if(x<=mid)    tr[q].l=insert(tr[p].l,l,mid,x);//x出现在左子树,修改左子树
45     else    tr[q].r=insert(tr[p].r,mid+1,r,x);//x出现在右子树,修改右子树
46     tr[q].s=tr[tr[q].l].s+tr[tr[q].r].s;//统计
47     return q;//返回当前分配使用的新节点编号
48 }
49 inl int query(int q,int p,int l,int r,int k)//查询区间
50 {
51     if(l==r)    return l;
52     int s=tr[tr[q].l].s-tr[tr[p].l].s;//线段树相减
53     int mid=l+r>>1;
54     if(k<=s)    return query(tr[q].l,tr[p].l,l,mid,k);//左儿子数字大于或等于k时,说明第k小数在右子树
55     return query(tr[q].r,tr[p].r,mid+1,r,k-s);//否则在左子树
56 }
57 void init()
58 {
59     //离散化
60     sort(num.begin(),num.end());
61     num.erase(unique(num.begin(),num.end()),num.end());
62     root[0]=build(0,num.size()-1);
63     
64 }
65 int main(void)
66 {
67     n=read(),m=read();
68     for(regi i=1;i<=n;i++)
69     {
70         a[i]=read();
71         num.push_back(a[i]);
72     }
73     init();
74     for(regi i=1;i<=n;i++)    root[i]=insert(root[i-1],0,num.size()-1,id(a[i]));
75     while(m--)
76     {
77         int l=read(),r=read(),k=read();
78         printf("%d\n",num[query(root[r],root[l-1],0,num.size()-1,k)]);
79     }
80     return 0;
81 }

可持续化的思想都基本是这样的,包括可持续化平衡树,可持续化堆(注意是思想,比如可持续化堆有的时候就不能动态开点)

 

标签:简要,int,线段,tr,ch,讲解,区间,主席,define
From: https://www.cnblogs.com/VL-house/p/18152605

相关文章

  • 面试不会算法和数据结构,经典面试题讲解来了!
    随着春招季节的临近,面试备战成为许多求职者的痛点。如何在激烈的竞争中脱颖而出,成为众多求职者思考的问题。学习Python编程与算法内容,成为面试开发、测试开发等热门岗位的基础。为了帮助大家更好地应对技术类面试挑战,霍格沃兹测试开发学社打造了Python编程和算法公开课,为同学们的......
  • MyBatis 核心配置讲解(上)
    大家好,我是王有志,一个分享硬核Java技术的互金摸鱼侠。前两篇的文章中我们分别介绍了MyBatis和MyBaits的应用组成,到这里基础篇的内容就结束了。从今天开始,我们正式进入MyBatis学习的第二阶段:MyBatis的应用部分。这个阶段从MyBatis应用程序的核心配置文件mybatis-conf......
  • 科里化函数实现以及应用场景讲解
    封装实现://函数柯里化封装(这个封装可以直接复制走使用)functioncurry(fn,args){varlength=fn.length;varargs=args||[];returnfunction(){newArgs=args.concat(Array.prototype.slice.call(ar......
  • 「主席树维护高精度」 CF464E The Classic Problem
    发现难点在于维护\(dis_u\)(起点\(s\)到\(u\)的最短路)。如果使用暴力高精度,复杂度会乘上一个\(len\)。考虑边权\(2^w\)的特殊性质,如果使用一个二进制串来维护\(dis\),那么加上边权\(2^w\)相当于将二进制下某一段\(1\)置为\(0\),然后再将一个\(0\)置为\(1\)。说白了......
  • 讲解GPU和CUDA编程的经典入门书籍
    作者:羊羊得亿-AIGC链接:来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。讲解GPU和CUDA编程的经典入门书籍|背景分析:GPU(图形处理单元)和CUDA(ComputeUnifiedDeviceArchitecture,统一计算架构)编程已经成为高性能计算和深度学习领域的重要工具。合......
  • Markdown学习的基本讲解
    Markdown学习的基本讲解标题二级标题字体hello,wordhello,wordhello,wordhello,word引用你好急急急,快递分割线图片超链接点击跳转列表wer1e5表格名字性别生日张三男1997.1.1代码1puite......
  • 【视频】R语言支持向量回归SVR预测水位实例讲解|附代码数据
    全文链接:https://tecdat.cn/?p=35914原文出处:拓端数据部落公众号分析师:MiaoqiaoWang当我们面对样本需要建立相应模型时,使用传统统计方法建立模型需要大量的样本数据,只有在样本量足够大时,该模型才具有一定的可靠性,而实际实验中,不一定每次实验都拥有足够大的样本,甚至是小样本,这......
  • 主席树的板子题
    题解方法1.可持久化线段树(主席树),代码有详细注释做法:先把值离散化在数值上建立线段树,维护每个数值区间中一共有多少个数问题1:如何求整体第K小数?答:二分,如果0~mid中有cnt数,cnt>=k,递归左边,如果cnt<k,递归右边,找k−cnt小的数。时间复杂的logn问题2:求【1,R】这个区间的第K......
  • 数码相框-测试&项目效果&部分代码讲解
    测试在makefile加上这句话意味着把警告当成错误处理:​​这里看视频跟着来就好了。input_manager.c详解#include<config.h>#include<input_manager.h>#include<string.h>staticPT_InputOprg_ptInputOprHead;staticT_InputEventg_tInputEvent;staticpthread_mu......
  • 通俗易懂的KMP理论讲解(含手求Next数组)
    通俗易懂的KMP理论讲解(含手求Next数组)1.KMP算法介绍KMP算法的核心是利用匹配失败后的信息,通过一个next数组,保存模式串中前后最长公共子序列的长度,尽量减少模式串与主串的匹配次数降低时间复杂度以达到快速匹配的目的。2.字符串的前后缀与公共前后缀2.1字符串的前缀字符串的......