[pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=63740442

向大(hei)佬(e)实力学(di)习(tou)

主席树主要用于处理区间的问题,其中区间不一定是单纯的区间,还是可以将题目中的一些信息转化一下(如 操作、时间等),即需要查询区间信息的数据,还需要用线段树用的题目,就可以用主席树。

以前有畏难心理,总觉得主席树好复杂,各种逃避。后来再次学习,总算是学会了基础的东西。

主席树通常是前缀和和线段树的结合,下面以一道经典题:区间第k大 为例

第k大,想到用值域线段树。区间可以转化为前缀,相减即可。即对每一个前缀节点建线段树,但如果暴力建,就完蛋了(^10),完美爆空间

仔细一想,每一个线段树和前一个相比,其实只修改了一个点。我们只需要新建树根到新增叶子节点路径上的点就可以了,其他的指向前一棵树的对应位置

放一张图来理解的更直观一些(灰色格子是虚点)



由此可见,空间复杂度为n*logn,可以接受

建树代码

void insert(Node *&ndn,Node *ndp,int le,int ri,int pos){//ndn当前的节点,注意要加&修改.ndp前一个节点,同步
ndn=newnode();
ndn->sum=ndp->sum+1;
if(le==ri) return ;
ndn->ls=ndp->ls,ndn->rs=ndp->rs;
int mid=(le+ri)>>1;
if(pos<=mid) insert(ndn->ls,ndp->ls,le,mid,pos);
else insert(ndn->rs,ndp->rs,mid+1,ri,pos);
}

这里有一个小技巧。因为指针常常指出去,就RE了,为了避免这一点以及不需要对root[0]先花2*n的空间建空树,我们这里用一个null的自环来表示空节点。妈妈再也不用担心我的指针写挂啦~(≧▽≦)/~

struct Node{
Node *ls,*rs;
int sum;
}*root[N],*null,pool[N*50],*tail=pool;
int main(){
null=++tail;
null->ls=null->rs=null;
null->sum=0;
root[0]=null;
}

然后就是查询,其实很简单,就像普通的值域线段树和前缀和区间查询就可以了

int query(Node *ndn,Node *ndp,int le,int ri,int pos){
if(le==ri) return le;
int lsum=ndn->ls->sum - ndp->ls->sum;//前缀和
int mid=(le+ri)>>1;
if(pos<=lsum) return query(ndn->ls,ndp->ls,le,mid,pos);
else return query(ndn->rs,ndp->rs,mid+1,ri,pos-lsum);
}

关键就解决了,至于区间第k大的题需要离散化,就不赘述了

完整代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std; const int N=100000+5;
const int oo=1000000000+7; struct Node{
Node *ls,*rs;
int sum;
}*root[N],*null,pool[N*50],*tail=pool;
struct aa{
int nu,val;
}hh[N];
int n,m,rank[N]; bool cmp1(aa a,aa b){
return a.val<b.val;
}
bool cmp2(aa a,aa b){
return a.nu<b.nu;
}
Node *newnode(){
Node *rt=++tail;
rt->ls=rt->rs=null;
rt->sum=0;
return rt;
}
void insert(Node *&ndn,Node *ndp,int le,int ri,int pos){
ndn=newnode();
ndn->sum=ndp->sum+1;
if(le==ri) return ;
ndn->ls=ndp->ls,ndn->rs=ndp->rs;
int mid=(le+ri)>>1;
if(pos<=mid) insert(ndn->ls,ndp->ls,le,mid,pos);
else insert(ndn->rs,ndp->rs,mid+1,ri,pos);
}
int query(Node *ndn,Node *ndp,int le,int ri,int pos){
if(le==ri) return le;
int lsum=ndn->ls->sum - ndp->ls->sum;
int mid=(le+ri)>>1;
if(pos<=lsum) return query(ndn->ls,ndp->ls,le,mid,pos);
else return query(ndn->rs,ndp->rs,mid+1,ri,pos-lsum);
}
int main(){
null=++tail;
null->ls=null->rs=null;
null->sum=0;
root[0]=null; scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&hh[i].val);
hh[i].nu=i;
}
sort(hh+1,hh+n+1,cmp1);
int sz=0,tmp=0;
for(int i=1;i<=n;i++){
if(tmp!=hh[i].val){
sz++;tmp=hh[i].val;
rank[sz]=tmp;hh[i].val=sz;
}
else hh[i].val=sz;
}
sort(hh+1,hh+n+1,cmp2);
for(int i=1;i<=n;i++)
insert(root[i],root[i-1],1,sz,hh[i].val);
int x,y,k;
while(m--){
scanf("%d%d%d",&x,&y,&k);
printf("%d\n",rank[query(root[y],root[x-1],1,sz,k)]);
}
return 0;
}

当然,主席树不只可以解决区间第k大的问题,灵活的变形后还可以解决不少问题。前段时间考了一套大佬的自编题,第一题就是变型的主席树,附上链接:

http://blog.csdn.net/coco56181712/article/details/75647313

可持久化线段树(主席树)(图文并茂详解)【poj2104】【区间第k大】的更多相关文章

  1. 静态区间第k大(归并树)

    POJ 2104为例 思想: 利用归并排序的思想: 建树过程和归并排序类似,每个数列都是子树序列的合并与排序. 查询过程,如果所查询区间完全包含在当前区间中,则直接返回当前区间内小于所求数的元素个数, ...

  2. 洛谷P3834 可持久化线段树(主席树)模板

    题目:https://www.luogu.org/problemnew/show/P3834 无法忍受了,我要写主席树! 解决区间第 k 大查询问题,可以用主席树,像前缀和一样建立 n 棵前缀区间的权 ...

  3. 可持久化线段树(主席树)——静态区间第k大

    主席树基本操作:静态区间第k大 #include<bits/stdc++.h> using namespace std; typedef long long LL; ,MAXN=2e5+, ...

  4. 主席树初步学习笔记(可持久化数组?静态区间第k大?)

    我接触 OI也快1年了,然而只写了3篇博客...(而且还是从DP跳到了主席树),不知道我这个机房吊车尾什么时候才能摸到大佬们的脚后跟orz... 前言:主席树这个东西,可以说是一种非常畸形的数据结构( ...

  5. 线段树简单入门 (含普通线段树, zkw线段树, 主席树)

    线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...

  6. 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题

    “队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄>     线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...

  7. 线段树专题2-(加强版线段树-可持续化线段树)主席树 orz! ------用于解决区间第k大的问题----xdoj-1216

    poj-2104(区间第K大问题) #include <iostream> #include <algorithm> #include <cstdio> #incl ...

  8. 主席树入门(区间第k大)

    主席树入门 时隔5个月,我又来填主席树的坑了,现在才发现学算法真的要懂了之后,再自己调试,慢慢写出来,如果不懂,就只会按照代码敲,是不会有任何提升的,都不如不照着敲. 所以搞算法一定要弄清原理,和代码 ...

  9. HDU 2665 Kth number(主席树静态区间第K大)题解

    题意:问你区间第k大是谁 思路:主席树就是可持久化线段树,他是由多个历史版本的权值线段树(不是普通线段树)组成的. 具体可以看q学姐的B站视频 代码: #include<cmath> #i ...

随机推荐

  1. nosetests

    1.nosetests 执行出测试报告 提前安装 插件nose-html系列插件   nosetests -v --with-html-output --html-out-file=报告名.html ...

  2. 牛客网暑期ACM多校训练营(第一场):D-Two Graphs

    链接:D-Two Graphs 题意:给出图G1和G2,求G2的子图中和G1同构的个数. 题解:只有8个点,暴力枚举G2的点每个排列,让G1映射到G2中,求出同构个数a.同构的G2就是在G1有边的对应 ...

  3. centos6系列问题

    一.NetworkManager启动问题 1.由于以后要支持e1000版虚拟化网卡,所有centos6镜像均按照NetworkManager服务,并设置开机自启动 2.虚机启动时,默认是Network ...

  4. ASP.NET——实现两个下拉框动态联动

    引入: 在网页中,我们经常会遇到下图中的情况.首先在下拉框中选择所在的省,选择之后,第二个下拉框会自动加载出该省中的市.这样设计极大的方便了用户的查找.那这是如何实现的呢? 1.建立数据库 " ...

  5. hdu 1969 Pie (二分法)

    Pie Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...

  6. Eclipse中一个Maven工程的目录结构 (MacOS)

    1. 为什么写这篇文章 在之前的javaSE开发中,没有很关注Eclipse工程目录下的环境,总是看见一个src就点进去新建一个包再写一个class.以后的日子中也没有机会注意到一个工程到底是怎么组织 ...

  7. 【CZY选讲·扩展LCS】

    题目描述 给出两个仅有小写字母组成的字符串str1 和str2,试求出两个串的最长公共子序列. 数据范围 |str1| ⩽ 1000; |str2| ⩽ 10^6 题解:    ①直接进行LCS( ...

  8. Codeforces 934.D A Determined Cleanup

    D. A Determined Cleanup time limit per test 1 second memory limit per test 256 megabytes input stand ...

  9. java8 获取对象中满足条件的金额之和

    记录一个小笔记:获取一个对象中,支付成功的金额之和: Long sum = list.stream().filter(o -> o.getStatus() == SUCCESS).mapToLo ...

  10. 口红游戏 插口红游戏 h5页面开发

    目前火热的口红机游戏,需要在设备前参与,然后成功后即可赢得口红,作为平台运营者来说还是比较重资产的,目前我们将它搬到了线上.每个人都可以远程玩这样的口红机游戏了.直接在手机微信里试玩,成功后,后台即可 ...