主席树总结(经典区间第k小问题)(主席树,线段树)
接着上一篇总结——可持久化线段树来整理吧。点击进入
这两种数据结构确实有异曲同工之妙。结构是很相似的,但维护的主要内容并不相同,主席树的离散化、前缀和等思想也要更难理解一些。
闲话
话说刚学习主席树的时候百度了一下,看到了“主席树”这一名字的由来——
线段树竟然是被一个黄嘉泰的大佬因不会划分树来代替的,,,,,因缩写是HJT取名为主席树= =!orz
我的内心瞬间感到了不安。。。。。。(看我的名字,就在右边)
能跟神犇有相同的缩写是何等荣幸!看来这个主席树我得好好学了。
那么接下来进入正题。
主席树
直接从最经典的应用——区间第\(k\)小问题开始吧。ZSY巨佬对这一问题的思路挺清晰的,本蒟蒻在这里也参考一下。%ZSY%请点这里
1. 静态区间第k小问题
洛谷题目传送门
即给出一个序列,每次询问求给定区间\([l,r]\)内第\(k\)小的值
思路分析
先不考虑每一个区间的情况,从最简单的查询整个区间\([1,r]\)的情况开始。
对数据离散化后,用一个线段树来维护,每个节点维护对应离散化后值区间的数的总个数\(size\)。自上至下进行询问操作时,判断当前点左子树的\(size\)与要查询的排名\(k\)的大小关系。如果小于等于,就到左子树中找,\(k\)不变。否则到右子树中找排名\(k-size\)的值。这与平衡树(Splay,Treap)等查询给定排名数的方法是基本一样的。
那对于所有可能的区间,又该怎样维护呢?我们其实只要\(N\)个线段树就好了,第\(i\)个线段树维护\([1,i]\)的情况。这里我们利用了前缀和的性质。查询\([l,r]\)就等于查询\([1,r]\)减去\([1,l-1]\)的对应的\(size\)没错吧。因为线段树是完全二叉树,具有结构稳定的性质,所以\(N\)个线段树长得是一样的,对应区间相减是可行的。
然而暴力开\(N\)个空间保准炸掉。这时候我们回头看看可持久化线段树是怎么做的。没错,从\([1,i-1]\)到\([1,i]\)也只变了一个值!于是同样只要新开\(log\)个节点,保存\(root_i\)到\(i\)对应叶子节点的路径就OK了。
拿洛谷题目里的样例来几张图吧,好理解些。
首先是离散化后的序列。
我们一开始要建一棵空线段树,除了有个结构,所有的\(size\)均为\(0\)。然后一个一个的添加线段树。加入\([1,1]\)的线段树后会是这样:
再加入\([1,2]\):
后面的手推一下吧。。。。。。
至此,\(N\)棵树就建好了,并且只用了\(N \log N\)的空间。
查询的时候存两个点,一开始为\(root_r\)和\(root_{l-1}\),两个\(size\)的差与\(k\)来比大小,两个点要同时往左/右跳。
更多细节就看代码吧。
#include<cstdio>
#include<algorithm>
using namespace std;
#define R register int
const int N=200009,M=5000009;
int P,a[N],b[N],rt[N],lc[M],rc[M],s[M];
#define G c=getchar()
inline void in(R&z)
{
register bool f=0;
register char G;
while(c<'-')G;
if(c=='-')f=1,G;
z=c&15;G;
while(c>'-')z=(z<<3)+(z<<1)+(c&15),G;
if(f)z=-z;
}//快读
void build(R&t,R l,R r)
{
t=++P;
if(l!=r)
{
R m=(l+r)>>1;
build(lc[t],l,m);
build(rc[t],m+1,r);
}
}//线段树操作,建一个空线段树
inline void insert(R*t,R u,R l,R r,R v)
{
while(l!=r)
{
s[*t=++P]=s[u]+1;//注意这里要+1
R m=(l+r)>>1;
if(v<=m)r=m,rc[*t]=rc[u],t=&lc[*t],u=lc[u];
else l=m+1,lc[*t]=lc[u],t=&rc[*t],u=rc[u];
}
s[*t=++P]=s[u]+1;
}//插入操作在可持久化线段树总结里面有更详细的介绍
inline int ask(R t,R u,R l,R r,R k)
{
while(l!=r)
{
R m=(l+r)>>1,v=s[lc[u]]-s[lc[t]];//作差
if(k<=v)r=m,t=lc[t],u=lc[u];//两个点一起跳
else l=m+1,t=rc[t],u=rc[u],k-=v;
}
return b[l];
}
int main()
{
R n,m,i,l,r,sz;
in(n);in(m);
for(i=1;i<=n;++i)
in(a[i]),b[i]=a[i];
sort(b+1,b+n+1);
sz=unique(b+1,b+n+1)-b-1;//离散化,排序去重
build(rt[0],1,sz);
for(i=1;i<=n;++i)
insert(&rt[i],rt[i-1],1,sz,lower_bound(b+1,b+sz+1,a[i])-b);//直接用STL的二分找对应值了
while(m--)
{
in(l);in(r);in(i);
printf("%d\n",ask(rt[l-1],rt[r],1,sz,i));
}
return 0;
}
2. 动态区间第k小问题
洛谷题目传送门
就是比上面那题多了个修改。。。。。。
改为树状数组维护前缀和,使修改复杂度降低。
详细题解在此
3.树上路径第k小问题
洛谷题目传送门
只维护树根节点(随便设)到每个节点的前缀和,查询时\(size[root,u]+size[root,v]-size[root,lca(u,v)]-size[root,father(lca[u,v))]\)即为\(u->v\)路径的\(size\)。
于是用倍增求LCA。
详细题解在此
主席树总结(经典区间第k小问题)(主席树,线段树)的更多相关文章
- HDU 2665.Kth number-可持久化线段树(无修改区间第K小)模板 (POJ 2104.K-th Number 、洛谷 P3834 【模板】可持久化线段树 1(主席树)只是输入格式不一样,其他几乎都一样的)
Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- Dynamic Rankings || 动态/静态区间第k小(主席树)
JYF大佬说,一星期要写很多篇博客才会有人看 但是我做题没有那么快啊QwQ Part1 写在前面 区间第K小问题一直是主席树经典题=w=今天的重点是动态区间第K小问题.静态问题要求查询一个区间内的第k ...
- ZOJ 2112 Dynamic Rankings(树状数组套主席树 可修改区间第k小)题解
题意:求区间第k小,节点可修改 思路:如果直接用静态第k小去做,显然我更改一个节点后,后面的树都要改,这个复杂度太高.那么我们想到树状数组思路,树状数组是求前缀和,那么我们可以用树状数组套主席树,求出 ...
- SPOJ-COT-Count on a tree(树上路径第K小,可持久化线段树)
题意: 求树上A,B两点路径上第K小的数 分析: 同样是可持久化线段树,只是这一次我们用它来维护树上的信息. 我们之前已经知道,可持久化线段树实际上是维护的一个前缀和,而前缀和不一定要出现在一个线性表 ...
- [luogu3834]静态区间第k小【主席树】
传送门:https://www.luogu.org/problemnew/show/P3834 题目描述 如题,给定N个整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 分析 很多人都说是用 ...
- 主席树铺垫——总区间第k小
题目描述(口糊) 先给定一个长度为n的数列,然后给m次操作,每次输入b,求第b小的数. 样例输入 5 7 4 10 9 23 5 1 2 3 4 5 样例输出 4 7 9 10 23 数据范围及温馨提 ...
- 主席树(可持久化线段树)静态区间第K小
传送门主席树 #include <bits/stdc++.h> #define int long long using namespace std; const int maxn=2e5+ ...
- poj2104 K-th Number区间第k小值 主席树
原来主席树就是可持久化线段树啊,刚知道,,, 作为一道裸题,还是必A的,然而一开始偷懒不写离散化跪了N多遍,后来在缪大的帮助下发现了这个问题,遂A之 ——又是这种破问题,实在不想说自己了 把n个数看成 ...
- BZOJ 3065 带插入区间K小值(sag套线段树)
3065: 带插入区间K小值 Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 4696 Solved: 1527[Submit][Status][Di ...
随机推荐
- WebSocket就是这么简单
前言 今天在慕课网上看到了Java的新教程(Netty入门之WebSocket初体验):https://www.imooc.com/learn/941 WebSocket我是听得很多,没有真正使用过的 ...
- eslint规则
碰到eslint报错, 把错误的提示拷贝在这里Ctrl + F找到复制到eslint.js里面就行了. "off"或者0,不启用这个规则 "warn"或者1,出 ...
- appium+Python 启动app(三)登录
我们根据前面的知识点,用uiautomatorviewer工具来获取我们当前的元素 (注:uiautomatorviewer 是 android sdk 自带的) 知识点:appium的webdriv ...
- 写好Java代码的30条经验总结(转)
成为一个优秀的Java程序员,有着良好的代码编写习惯是必不可少的.下面就让我们来看看代码编写的30条建议吧. (1) 类名首字母应该大写.字段.方法以及对象(句柄)的首字母应小写.对于所有标识符,其中 ...
- 试用MarkDown
自定义界面风格 可以在设置中选择日间,或者夜间模式进行定义.具体的定义项的说明,可以查看菜单栏 (Windows版本位于托盘按钮上) 自定义的帮助. MarkEditor几乎所有跟色彩有关的界面,都已 ...
- 在ASP.Net Core下,Autofac实现自动注入
之前使用以来注入的时候,都是在xml配置对应的接口和实现类,经常会出现忘了写配置,导致注入不生效,会报错,而且项目中使用的是SPA的模式,ajax报错也不容易看出问题,经常会去排查日志找问题. 于是在 ...
- 02 浅析Spring的AOP(面向切面编程)
1.关于AOP AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善.O ...
- UVA - 11995 I Can Guess the Data Structure!(模拟)
思路:分别定义栈,队列,优先队列(数值大的优先级越高).每次放入的时候,就往分别向三个数据结构中加入这个数:每次取出的时候就检查这个数是否与三个数据结构的第一个数(栈顶,队首),不相等就排除这个数据结 ...
- HDU - 1907 John 反Nimm博弈
思路: 注意与Nimm博弈的区别,谁拿完谁输! 先手必胜的条件: 1. 每一个小游戏都只剩一个石子了,且SG = 0. 2. 至少有一堆石子数大于1,且SG不等于0 证明:1. 你和对手都只有一种选 ...
- .net Winfrom 僵尸窗口 的问题
执行顺序如下: Form1 form1 =new Form1(); form1.ShowDialog(); Form2 form2= Application.OpenForms["Form2 ...