zoukankan      html  css  js  c++  java
  • 可持久化线段树/主席树 总结

    前言

    可持久化数据结构其大多数有两种用法,但是其本质都是维护了一个“前缀数据结构”。

    第一种用法是对于可以差分的信息(不只是序列上的差分,也可以是树上差分),我们就可以直接通过“前缀和”相减来得到对应区间的信息,从而维护。

    第二种用法比较纯粹,就是在我们需要访问这个数据结构每一个历史版本的时候,我们能够直接取出来使用了。

    另外,主席树这个名称特指“可持久化值域线段树”,之后就不再提了。

    正文

    基础题

    最常规的题目,大多是模板。

    例题

    BSOJ5526&LOJ519 数学上来先打表

    5526【Loj519】数学上来先打表

    操作树+值域分块+并查集,然后就比较容易了。

    具体不再赘述,请看题解。

    BSOJ6733【模板】可持久化数组

    【模板】可持久化数组

    模板题,使用可持久化线段树维护即可。

    BSOJ6735【模板】可持久化栈

    BSOJ6735【模板】可持久化栈

    似乎卡了空间,于是直接操作树建出来做。

    BSOJ6734 【模板】可持久化并查集

    BSOJ6734 【模板】可持久化并查集

    可持久化线段树维护 (fa) 数组,并查集按秩合并即可。

    BSOJ6729 【模板】区间第k小值(主席树)

    BSOJ6729 【模板】区间第k小值

    模板题,做法思路就是主席树维护每一个前缀下标构成的线段树,然后发现每个数的个数可以差分,于是可以差分来实现区间询问,然后剩下的就是线段树上二分了。

    SP11470 TTM - To the moon

    SP11470 TTM - To the moon

    可持久化线段树区间修改模板。

    注意可持久化线段树尽量还是需要标记永久化,因为如果是标记下传的话,每次下传的时候还要多复制一下所有的标记,空间消耗极大,所以一般使用标记永久化,所以注意写法。

    P1903 [国家集训队]数颜色 / 维护队列

    P1903 [国家集训队]数颜色 / 维护队列

    讲过一万次的题目,区间数颜色问题。

    我们之前使用的是离线后树状数组的做法,这里介绍在线的主席树。

    还是对于每一个点维护前驱的位置,然后询问就是在两个树上差分,询问哪些下标的权值在区间([1,l-1])当中即可。

    常规题目

    稍微常规一点的题目,没有特别 (trick)

    BSOJ3876【BZOJ3207】花神的嘲讽计划Ⅰ

    BSOJ3876【BZOJ3207】花神的嘲讽计划Ⅰ

    哈希过后发现本质就是查询一个区间内是否含有某个数,直接主席树解决。

    BSOJ3401【模拟试题】建筑

    BSOJ3401【模拟试题】建筑

    本质就是询问一个下标区间([l,r]),且权值在值域区间 ([p,q]) 的第 (k) 大的数。

    可以直接主席树上二分解决。

    BSOJ2895【BZOJ2223】PATULJCI

    BSOJ2895【BZOJ2223】PATULJCI

    直接主席树上二分即可,因为是一半,所以必然只会有一边区间满足,要不然就是两边都不满足。

    BSOJ1583【BZOJ3524】Couriers

    同上。

    BSOJ2251【BZOJ3744】Gty的妹子序列

    BSOJ2251【BZOJ3744】Gty的妹子序列

    询问区间逆序对,强制在线。

    可以先离散化,然后分块维护整块,对于散块主席树维护。

    存在在线的分块(O(nsqrt{n}))做法,和同样复杂度的离线莫队做法。

    CF853C Boredom

    CF853C Boredom

    稍微容斥一下就会变成一个二维数点问题,可以直接主席树。

    P3168 [CQOI2015]任务查询系统

    P3168 [CQOI2015]任务查询系统

    主席树模板,其实就是对于每一个下标维护一下值域线段树,然后线段树上二分查询全局第 (k) 小即可。

    BSOJ2781【模拟试题】区间第k大(版本2)

    BSOJ2781【模拟试题】区间第k大(版本2)

    单点修改区间第 (k) 大,主席树套树状数组维护。

    不想写了,离线整体二分做的。

    P3332 [ZJOI2013]K大数查询

    P3332 [ZJOI2013]K大数查询

    可以整体二分+线段树,也可以主席树+树状数组,相对于常规的就是多了一个区间修改而已。

    经典题/Trick题/妙题

    P2839&BSOJ3867 [国家集训队]middle

    P2839&BSOJ3867 [国家集训队]middle

    关于中位数的常用套路:二分中位数,然后小于的变成 (-1) ,剩下的变成 (1) ,然后数和即可,和大于0证明当前数小了,和小于0证明当前数大了。

    但是在这道题当中,相当于要让中位数尽可能大,于是我们就要尽可能多的取到 (1) ,那么我们这道题因为中间的区间是固定的,那么变化的就只有前后两边。

    那么我们考虑线段树维护,但是这样的话我们每次询问都要二分再维护一次,这样很慢。

    然后我们发现其实 (mid) 的变化量如果只有 (1) 的话,只有很少位置会改变,于是考虑可持久化线段树,对于每一个 (mid) 值维护一个线段树。

    然后直接去线段树上询问即可。

    太妙了,这道题可以说是完美应用到了可持久化的性质。

    P4602&BSOJ5586 [CTSC2018]混合果汁

    P4602&BSOJ5586 [CTSC2018]混合果汁

    和上一题有异曲同工之妙,这道题可以离线,那么也可以使用整体二分来做,这里讲主席树做法。

    显然答案具有单调性,于是就可以二分,那么我们直接考虑对每一个值建立线段树,但是开不下,所以考虑主席树,然后最后求答案就在每一个询问上二分然后线段树上二分即可。

    同样是巧妙地应用了主席树,感觉这类二分题都能这样做?

    P4587&BSOJ6169 [FJOI2016]神秘数

    P4587 [FJOI2016]神秘数

    这题比较 (trick),首先我们考虑怎么来求神秘数,发现我们对于每一个数可以这样来更新:

    对于 (x) ,如果我们可以得到前 (nowsumge x),那么我们就可以得到:前(nowsum o nowsum+x)

    那么我们就可以通过不停地维护 (nowsum) 直到不存在这样的 (x) 为止。

    发现这样需要建立值域线段树,然后这里又是区间询问,所以主席树即可。

    P3293 [SCOI2016]美味

    P3293 [SCOI2016]美味

    按位贪心,可以可持久化(01trie)或者主席树,这里说主席树做法。

    对于最大异或和的按位贪心,其实我们除了一般的在 (01trie) 上二分,还有一个就是把当前位对应的值域区间直接写出来,然后再直接维护整个值域。

    也就是说,假设我们要求的是这里的 (b_i xor (a_j+x_i))

    那么相当于如果 (b) 的这一位是 (1)(a_j) 就落在 ([now+(1<<k)-x,now+(1<<(k+1))-1-x]) 这个区间当中会让这一位成为 (1) ,所以我们就直接在值域线段树上查询这一段是否存在数即可。

    然后这样一位一位累加下去即可。

    因为这里是区间查询,所以我们需要把我们的值域线段树可持久化,也就是主席树了。

    这道题对于异或和的处理比较新也比较 (trick)

    P3241 [HNOI2015]开店

    P3241 [HNOI2015]开店

    可以点分树,这里讲可持久化线段树做法。

    先这样转化一下:

    然后现在重点是求点权在一段区间内的点和 (u) 之间的(LCA)的所有(dis)和。

    于是我们按照点权从小到大来建立可持久化线段树,每一个都维护前缀,也就是说我们如果每加进来一个点,那么我们就在这个点到根的路径上所有点加上(1)

    然后询问就是直接询问当前点到根的路径上的区间和,然后因为是区间询问,所以直接用 ([1,r]) 的答案减去 ([1,l-1]) 的答案即可。

    这道题是前缀数据结构维护树上路径的 (trick)

    BSOJ1781【BZOJ3514】GERALD07加强版

    BSOJ1781【BZOJ3514】GERALD07加强版

    似乎可以直接线段树分治,但是这里强制在线,于是考虑在线做法。

    我们考虑 (lCT) 来求出每一条边加入的时候,如果当前构成了环,那么我们就删掉环上最早出现的边,并称这条删除的边是可以“替代”当前边的,就是把当前边 (pre[i]) 设为 (tmp) (删除的边的编号)。

    然后我们发现问题变成了:询问一段区间内小于某个数的个数,直接主席树即可。

    这道题重点在于 (LCT) 的那一步转化,或许这题更应该是个 (LCT) 题(?)。

    P4559 [JSOI2018]列队

    P4559 [JSOI2018]列队

    容易发现这题如果我们保持学生的相对顺序不变,答案肯定是不劣的,所以我们就让学生保持排队后的相对顺序不变即可。

    那么肯定会出现一个分界点,这个点左边的学生往右跑,右边的往左跑。

    BSOJ1783【BZOJ3658】Jabberwocky

    BSOJ1783【BZOJ3658】Jabberwocky

    发现其实最优的策略就是只有一个颜色不包含。

    于是可以考虑枚举这个颜色,然后再发现这个线一定落在点上最优,于是考虑枚举每一个点。

    但是我们还要保证选的区间不包含第二个这种颜色的点,才能保证不包含这个颜色。

    于是我们可以这样来决定左右边界,也就是如果现在我们要选上面,那么我们当前点对应可以选的 (x) 坐标区间就在 ([pre_i+1,suf_i-1]) ,且这两个数组只是对于这个点及(y)坐标大于等于这个点的那些点来说的,因为这样才可以保证这个颜色完全没有。

    然后我们就是一个二维数点问题了,可以可持久化线段树来做。

    BSOJ5381【BZOJ3956】Count

    BSOJ5381【BZOJ3956】Count

    我们发现,可以利用单调栈,对于每一个点作为 (i) ,求出对应的 (j) 的可行区间。

    于是这就是询问一段区间内,且权值满足在一段区间的数的个数,那么可持久化线段树即可。

    这种对于每一个左端点,求出右端点所在区间,然后可持久化线段树询问的技巧也很好用。

    P3722 [AH2017/HNOI2017]影魔

    P3722 [AH2017/HNOI2017]影魔

    和上一题比较类似,就是单调栈求出一个区间对另一个区间的贡献,一个点的贡献,然后主席树维护即可。

    这道题可以离线,于是也可以离线后树状数组。

    CF464E The Classic Problem

    CF464E The Classic Problem

    首先我们发现肯定不能直接维护因为会炸,但是这里的数以二进制给出,容易想到维护二进制串来表示我们的数。

    但是这样做的话,最短路会用到的比较和做加法效率都很低了,是 (O(n)) 单次的。

    于是考虑使用数据结构来维护这个二进制串。

    发现对于动态开点线段树,其实就很好来维护这个比较,直接在线段树上二分即可。

    但是加法的话就相当于要复制这个线段树,然后再给某一位加一。

    等等,复制线段树?

    于是我们可以想到可持久化线段树,并且这里的单次修改很少。

    然后我们考虑怎么来做加法。

    我们先线段树上二分找到第一个零的位置,然后把这个点单点修改成(1),再把前面那一段区间设置成零即可。

    后面这个区间修改我们可以使用一个技巧。这里有两种实现方法,第一种是建立一个全0的树,然后直接把儿子指过去就行了,第二个办法是直接设为空节点,默认就是 (0) 了。

    这样做复杂度是显然正确的。

    然后就是最短路模板了,(Dijkstra) 即可,时间复杂度 (O(nlog^2n))

    BSOJ3865 BZOJ2588 SPOJ10628 P2633 Count on a tree

    BSOJ3865 BZOJ2588 SPOJ10628 P2633 Count on a tree

    套路题,就是维护树上前缀主席树,然后询问就是树上差分的思路在线段树上二分即可。

    BSOJ3405【模拟试题】路径第k大

    BSOJ3405【模拟试题】路径第k大

    今有一n点之无根树,点上有权,试问路径之第k大点几何?

    询问路径第 (k) 大,可以直接建立前缀主席树,然后树上差分在线段树上二分第 (k) 大即可。

    P4216 [SCOI2015]情报传递

    P4216 [SCOI2015]情报传递

    单点修改,询问路径上权值小于某个数的个数。

    两个做法,一是先读完直接改完所有点权,然后做静态的差分+主席树即可维护,复杂度 (O(nlogn)) ,另外一种是,就是树剖+主席树,这个做法就比较显然,但是劣一些,是(O(nlog^2n))的。

    P3302 [SDOI2013]森林

    P3302 [SDOI2013]森林

    发现如果只有第一个操作那么就是可持久化线段树,上面已经提到过了。

    现在有了第二个操作,其实看到“合并”就启示我们想启发式合并了,于是我们直接启发式合并,然后暴力更新小的那个树的整个子树的信息即可。

    SP1487 PT07J - Query on a tree III

    SP1487 PT07J - Query on a tree III

    查询子树第 (k) 大,没什么意思,直接 (dfn) 序就是区间第 (k) 大了。

    P1552 [APIO2012]派遣

    P1552 [APIO2012]派遣

    左偏树题,放到这里就没做了。

    CF893F Subtree Minimum Query

    CF893F Subtree Minimum Query

    换成离线的话就是经典的(dsu on tree)了,可惜换不得。

    那么在线其实我们发现这里前缀构造还是(dfn)序构造主席树都不行了,那么怎么办呢?

    我们发现其实一个点子树内且距离它深度不超过 (k) 其实可以转化成两个限制,一是(dfn)序在一段区间内,二是深度在一段区间内。

    两个限制,然后询问最小值,直接可持久化线段树维护区间最小值即可,这里按照每一个深度建立或者按照(dfn)序建立可持久化线段树都可以。

    BSOJ4205【BZOJ4771】七彩树

    BSOJ4205【BZOJ4771】七彩树

    可持久化线段树合并,具体见题解,懒得讲。

    CF226E Noble Knight's Path & BSOJ4102【2014NOI模拟5】旅行

    CF226E Noble Knight's Path & BSOJ4102【2014NOI模拟5】旅行

    询问路径上权值在一段区间内,且位于路径上第 (k) 个满足上述条件的点的编号。

    先树剖,然后不断地跳,先从(u o lca) ,然后(lca o v) ,直到当前重链满足要求的点的个数比当前的 (k) 要大,然后在这条重链上二分即可,注意方向。

    思路比较简单,实现比较麻烦,看着都不想写。

    P4175 [CTSC2008]网络管理

    P4175 [CTSC2008]网络管理

    树上带修路径第 (k) 大。

    可以(dfn)序直接转化成序列问题,主席树套树状数组解决,复杂度(nlog^3n)

    也可以直接主席树套树状数组,复杂度(nlog^2n),具体见题解第一篇。

    然后也可以整体二分和树上带修莫队之类的,暂不赘述。

    BSOJ4445【FJMTC2015#4】神牛的养成计划

    BSOJ4445【FJMTC2015#4】神牛的养成计划

    这题可以用两个 (trie) 直接把问题转化成:询问((u,v)):有多少个编号满足在第一棵树(u)的子树内,在第二棵树(v)的子树内。

    两维限制,很明显是一个二维数点,可持久化线段树维护即可。

  • 相关阅读:
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:Listener介绍
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:Filter介绍
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:JSP2的自定义标签
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:Servlet介绍
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:JSP脚本的9个内置对象(2)
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:JSP脚本的9个内置对象(1)
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:JSP的3个编译指令
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:JSP4种基本语法
    HDU6118:度度熊的交易计划(入门级最小费用可行流)
    BZOJ-4819: 新生舞会(01分数规划+费用流)
  • 原文地址:https://www.cnblogs.com/Akmaey/p/14901186.html
Copyright © 2011-2022 走看看