权值线段树

顾名思义,就是以权值为下标建立的线段树。

现在让我们来考虑考虑上面那句话的产生的三个小问题:

1. 如果说权值作为下标了,那这颗线段树里存什么呢?

————— 这颗线段树中, 记录每个值出现的次数

2.权值很大怎么办?数组空间不够啊

————— 可以先离散化,再记录

3.那权值线段树到底是用来干嘛的呢?

————— 可以快速求出第k小值(其实主要还是为了主席树做铺垫啦)

那第k小值该怎么求呢???

从树根依次往下

若当前值K大于左儿子的值,则将K-=左儿子的值,然后访问右儿子

若当前值K小于左儿子的值,则直接访问左儿子

直到访问到叶子节点时,那么该节点所代表的那个数就是要求的第k小值

(因为其实节点中存的值是该值域区间的数字出现次数,所以第k小值前面一定会有k-1个数出现过)

代码就不给了

可持久化线段树

普通的线段树单点修改操作与区间查询自然不是问题

可是

假如当前询问若干修改操作之前的区间呢???

仔细想想

.

.

.

最暴力的做法无疑是对于每个修改操作重开一个线段树,

可是...这样显然空间开不下

那我们能不能优化一下呢

我们看看对于一次单点修改,这颗线段树操作前和操作后有什么不同吧

有点小丑,凑合着看

观察一下这两颗树,发现它们有区别的地方仅仅在于红色的方框

哎??? 这不是此次操作修改的目标元素到根的路径吗

既然只有这条路径变了,那我们就只复制这条路径好了,不用再复制整棵树了

所以空间就能大大的缩小了(log级)

代码 (洛谷模板)

#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
#define get getchar()
#define in inline
in int read()
{
int x=1,t=0; char ch=get;
while((ch<'0' || ch>'9') && ch!='-') ch=get;
if(ch=='-') ch=get,x=-1;
while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
return t*x;
}
const int _=1e6+6;
int n,m,a[_],tot,root[_<<5],ls[_<<5],rs[_<<5],val[_<<5]; // ls == leftson,rs == rightson
in int build(int l,int r)
{
int now=++tot;
if(l==r)
{
ls[now]=rs[now]=0;
val[now]=a[l];
return now;
}
int mid=(l+r)>>1;
ls[now]=build(l,mid);
rs[now]=build(mid+1,r);
return now;
} //初始时的线段树
in int add(int k,int l,int r,int x,int t)
{
int now=++tot;
if(l==r)
{
val[now]=t;
ls[now]=rs[now]=0;
return now;
} //到了目标点,修改它
ls[now]=ls[k],rs[now]=rs[k];
int mid=(l+r)>>1;
if(x<=mid) ls[now]=add(ls[now],l,mid,x,t); //若目标点在原树的左子树上,则新建左儿子
else rs[now]=add(rs[now],mid+1,r,x,t); //若在右儿子上,同理
return now;
} //修改并添加新路径
in int query(int k,int l,int r,int x)
{
if(l==r) return val[k];
int mid=(l+r)>>1;
if(x<=mid) return query(ls[k],l,mid,x);
else return query(rs[k],mid+1,r,x);
} //查询
int main()
{
n=read(),m=read();
for(re int i=1;i<=n;i++)
a[i]=read();
root[0]=build(1,n);
for(re int i=1;i<=m;i++)
{
int v=read(),o=read();
if(o==1)
{
int x=read(),y=read();
root[i]=add(root[v],1,n,x,y);
}
else
{
int x=read();
cout<<query(root[v],1,n,x)<<endl;
root[i]=root[v];
}
}
/*for(re int i=0;i<=10;i++)
{
cout<<"case #"<<i<<": ";
for(re int j=1;j<=n;j++)
cout<<query(root[i],1,n,j)<<' ';
cout<<endl;
}//打印每个历史版本 */
return 0;
}
/*
9.30 By yzhx
*/

静态主席树

可以用来求区间第k小/大值

说白了,就是把我们上面讲到的两个东西加起来,也就是用 可持久化权值线段树

再来看建树的具体步骤:

1.建一颗空线段树

2.依次把每个值加入这颗线段树(看做是一个修改操作)

查询:

(若当前查询的区间 l~r)

则直接把历史版本r 与 历史版本l-1, 直接加减,就能得到当前这个区间每个数出现的情况了

代码 (洛谷模板)

#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
#define in inline
#define get getchar()
in int read()
{
int t=0,x=1; char ch=get;
while((ch<'0' || ch>'9') && ch!='-') ch=get;
if(ch=='-') ch=get,x=-1;
while( ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
return t*x;
}
const int _=2e5+5;
int tot,cnt,n,m,a[_],b[_],sum[_<<6],ls[_<<6],rs[_<<6],root[_];
in int build(int l,int r)
{
int now=++cnt;
if(l==r)
{
sum[now]=ls[now]=rs[now]=0;
return now;
}
int mid=(l+r)>>1;
ls[now]=build(l,mid),rs[now]=build(mid+1,r);
return now;
} //建一颗空树
in int add(int k,int l,int r,int x)
{
int now=++cnt;
if(l==r)
{
ls[now]=rs[now]=0;
sum[now]=sum[k]+1;
return now;
}
int mid=(l+r)>>1;
ls[now]=ls[k],rs[now]=rs[k],sum[now]=sum[k];
if(x<=mid) ls[now]=add(ls[k],l,mid,x);
else rs[now]=add(rs[k],mid+1,r,x);
sum[now]=sum[rs[now]]+sum[ls[now]];
return now;
} //加入每个元素
in int query(int k1,int k2,int l,int r,int x)
{
if(l==r) return a[l];
int mid=l+r>>1;
int t=sum[ls[k2]]-sum[ls[k1]];
if(x<=t) return query(ls[k1],ls[k2],l,mid,x);
else return query(rs[k1],rs[k2],mid+1,r,x-t);
} //查询
int main()
{
n=read(),m=read();
for(re int i=1;i<=n;i++)
b[i]=a[i]=read();
sort(a+1,a+n+1);
tot=unique(a+1,a+n+1)-(a+1);
root[0]=build(1,n);
for(re int i=1;i<=n;i++)
{
// if(i<=tot) cout<<a[i]<<' ';
int x=lower_bound(a+1,a+tot+1,b[i])-a;
root[i]=add(root[i-1],1,tot,x);
}
//cout<<endl;
for(re int i=1;i<=m;i++)
{
int l=read(),r=read(),k=read();
printf("%d\n",query(root[l-1],root[r],1,tot,k));
}
}

权值线段树&&可持久化线段树&&主席树的更多相关文章

  1. 主席树||可持久化线段树+离散化 || 莫队+分块 ||BZOJ 3585: mex || Luogu P4137 Rmq Problem / mex

    题面:Rmq Problem / mex 题解: 先离散化,然后插一堆空白,大体就是如果(对于以a.data<b.data排序后的A)A[i-1].data+1!=A[i].data,则插一个空 ...

  2. BZOJ4771七彩树——可持久化线段树+set+树链的并+LCA

    给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点.每个节点都被染上了某一种颜色,其中第i个节 点的颜色为c[i].如果c[i]=c[j],那么我们认为点i和点j拥有相同的颜色.定义dept ...

  3. [BZOJ 4771]七彩树(可持久化线段树+树上差分)

    [BZOJ 4771]七彩树(可持久化线段树+树上差分) 题面 给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点.每个节点都被染上了某一种颜色,其中第i个节点的颜色为c[i].如果c[i] ...

  4. 主席树||可持久化线段树||离散化||[CQOI2015]任务查询系统||BZOJ 3932||Luogu P3168

    题目: [CQOI2015]任务查询系统 题解: 是一道很经典的题目.大体思路是抓优先级来当下标做主席树,用时刻作为主席树的版本.然而优先级范围到1e7去了,就离散化一遍.然后把每个事件的开始(s). ...

  5. [luogu3919]可持久化数组【主席树】

    链接:https://www.luogu.org/problemnew/show/P3919 分析 很明显我们可以用主席树来维护,所谓主席树就是可持久化线段树,能够查询历史版本而且可以实现修改操作,反 ...

  6. SPOJ DQUERY树状数组离线or主席树

    D-query Time Limit: 227MS   Memory Limit: 1572864KB   64bit IO Format: %lld & %llu Submit Status ...

  7. 最大矩阵覆盖权值--(静态连续最大子段 (线段树) )-HDU(6638)Snowy Smile

    这题是杭电多校2019第六场的题目 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6638 题意:给你平面上n个点,每个点都有权值(有负权),让你计算一 ...

  8. 主席树[可持久化线段树](hdu 2665 Kth number、SP 10628 Count on a tree、ZOJ 2112 Dynamic Rankings、codeforces 813E Army Creation、codeforces960F:Pathwalks )

    在今天三黑(恶意评分刷上去的那种)两紫的智推中,突然出现了P3834 [模板]可持久化线段树 1(主席树)就突然有了不详的预感2333 果然...然后我gg了!被大佬虐了! hdu 2665 Kth ...

  9. 归并树 划分树 可持久化线段树(主席树) 入门题 hdu 2665

    如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k ...

随机推荐

  1. 坚果云+svn实现异地非局域网个人代码版本管理

    原理大概是A地的设备作为服务端创建仓库,将仓库传上坚果云,同步到B地,再拉取仓库的代码

  2. spring注解(Component、依赖注入、生命周期、作用域)

    1.注解 注解就是一个类,使用@加上注解名称,开发中可以使用注解取代配置文件 2.@Component 取代<bean  class="">,@Component 取代 ...

  3. 对之前IoT项目的完善

    博文有点长,因为是两个大项目(四个小项目)放一起了,不过都很适合新手小白(有源程序的情况),也可以再接 OLED 屏,就是前几篇博客的操作 一.esp8266 读取 DHT11 数据并通过微信小程序发 ...

  4. 基础篇:java基本数据类型

    1:java几种基本数据类型大小 关键字 类型 位数 (8位一字节) 取值范围(表示范围) byte 整型 8 -2^7 ~ 2^7-1 short 整型 16 -2^15 ~ 2^15-1 int ...

  5. uni-app支付功能

    扫码查看原文 前言 近期一直在使用APP开发多端应用,IOS的APP.安卓的APP和H5网页,其中开发的APP使用到了微信和支付宝的支付,在此给大家分享出来,一起使用 前置条件: 开发环境:windo ...

  6. Centos-bash-4.1$

    错误: -bash-4.1$ where? 登录Centos时候,会显示4行这样的错误信息-bash-4.1$ why? 1. 该用户家目录缺少 .bashrc .bash_logout .base_ ...

  7. VARCHART XGantt如何计算截止日期

    甘特图从1998年的第一个商用版本开始就致力于计划编制和项目管理方面控件的研究和开发,经过20多年的积累和沉淀,目前可为软件开发商和最终用户提供最顶级的计划编制和项目管理的控件产品,帮助用户快速的整合 ...

  8. spring-boot-route(四)全局异常处理

    在开发中,我们经常会使用try/catch块来捕获异常进行处理,如果有些代码中忘记捕获异常或者不可见的一些异常出现,就会响应给前端一些不友好的提示,这时候我们可以使用全局异常处理.这样就不用在代码中写 ...

  9. Java面试题系列 ----- Java基础面试题(91道)

    更多详情点击查看,点这里!这里!!这里!!! 文末获取所有面试PDF文档! Java概述 1. 何为编程 编程就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到结果的过程. 为了 ...

  10. IDEA设置External Tools之Javap反编译字节码

    通过Jdk的命令javap可以反编译查看字节码,但是在使用idea的时候一直用命令行去操作不太好操作,而且因为idea会把class码 放在target里面,经常会忘记切换目录.这个时候idea的Ex ...