树链剖分专题2
专题2主要包括:
1.线段树复杂合并(颜色段 差值 lcis)
2.线段树维护子树
3.颜色转换二进制维护
bzoj 2243
题目要求
给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。
请你写一个程序依次完成这m个操作。
第一行包含2个整数n和m,分别表示节点数和操作数;
第二行包含n个正整数表示n个节点的初始颜色
下面 行每行包含两个整数x和y,表示x和y之间有一条无向边。
下面 行每行描述一个操作:
“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。
对于每个询问操作,输出一行答案。
参考AC代码
|
|
思路
用线段树维护颜色段 注意合并的时候若l的右端点和r的左端点相同 那么相加的总段数-1
注意:
1.本题颜色可以取到0 所以lazy的初始化要为-1而不能是0
2.结构体需要初始化l r和num 否则quertco里的T l和r会出错
HDU 5052
题目要求
一棵树,每个结点有个初始的权值,点的权值代表在该点的鸡肉的价格。
对于一个询问X, Y, V。
找到X到Y的路径,可以选择在路径上一个点I买鸡肉,然后在点J卖掉,要求J必须在I之后访问。
那么你就可以赚取差价,问最大差价是多少。然后这条路径上的点的权值全部增加V。
参考AC代码
|
|
思路
这道题,首先考虑一条链的版本,我们用线段树来维护,先从区间合并来考虑。
对于当前结点,那么结点的最优值肯定来自于左孩子和右孩子的最优值,以及区间合并的最优值。
区间合并的时候,我们肯定是在左孩子的区间买,在右孩子的区间卖,最大差值肯定是右孩子的最大值-左孩子的最小值。
区间合并搞定了。更新操作也很容易,加个懒惰标记,由于区间内全部加上V值,对于区间内的差价是没有影响的,所以直接累加到标记上就可以。
那么来到这题,变成多条链的合并。我们会发现,如果是从X到Y的方向,求解方法同上。
但是如果我们是Y到X的方向,我们应该反过来,即最大差值=左孩子的最大值-右孩子的最小值。
HDU 3308
题目要求
本题虽然不是树链剖分 但是是建立在下一题的基础上
一条线段 给出点权 两种操作:
1.修改点权
2.求出区间l-r内的lcis(最长连续上升子序列)
参考AC代码
|
|
思路
线段树维护lcis
lsum表示左端点向右延伸的最大值 rsum表示右端点向左延伸的最大值
sum表示区间的最大值
注意pushup的合并操作即可 判断mid处是否可以衔接
下标从0开始 方便计算点的索引++
query里的合并注意mid-L+1 R-mid而不是l和r
HDU 4718
题目要求
本题建立在上一题的基础上 求树上两点间的lcis
参考AC代码
|
|
思路
树上的问题 用树链剖分转换成线性结构 就跟上题类似了
由于树链剖分向上爬的特殊性 需要继续2个方向的最大值 所以参数如下:
LL:左端点下降的最远距离
LR:左端点上升的最远距离
RL:右端点上升的最远距离
RR:右端点下降的最远距离
len:从左向右的最大长度
ren:从右向左的最大长度
同时为了merge的顺利实现 需要记录l和r两个边界的值
具体merge实现与上题类似
bzoj 4034
题目要求
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1
行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。再接下来 M 行,每行分别表示一次操作。其中
第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
参考AC代码
|
|
思路
由于有某节点到根节点的操作 这是一条链 所以使用树链剖分 也有子树的操作 在dfs2加入in和out数组求出dfs序去维护子树
注意开LL 并且本题是区间更新区间求和 sum需要加左右端点区间内的每一个值 所以注意pushdown和sum的写法
bzoj 4196
题目要求
你决定设计你自己的软件包管理器。不可避免地,你要解决软件包之间的依赖问题。如果软件包A依赖软件包B,那么安装软件包A以前,必须先安装软件包B。
同时,如果想要卸载软件包B,则必须卸载软件包A。现在你已经获得了所有的软件包之间的依赖关系。而且,由于你之前的工作,除0号软件包以外,在你的管理器
当中的软件包都会依赖一个且仅一个软件包,而0号软件包不依赖任何一个软件包。依赖关系不存在环(若有m(m≥2)个软件包A1,A2,A3,…,Am,其中A1依赖A2,A2依赖A3
,A3依赖A4,……,Am−1依赖Am,而Am依赖A1,则称这m个软件包的依赖关系构成环),当然也不会有一个软件包依赖自己。
现在你要为你的软件包管理器写一个依赖解决程序。根据反馈,用户希望在安装和卸载某个软件包时,快速地知道这个操作实际上会改变多少个软件包的安装状态
(即安装操作会安装多少个未安装的软件包,或卸载操作会卸载多少个已安装的软件包),你的任务就是实现这个部分。注意,安装一个已安装的软件包,
或卸载一个未安装的软件包,都不会改变任何软件包的安装状态,即在此情况下,改变安装状态的软件包数为0。
输入文件的第1行包含1个正整数n,表示软件包的总数。软件包从0开始编号。
随后一行包含n−1个整数,相邻整数之间用单个空格隔开,分别表示1,2,3,…,n−2,n−1号软件包依赖的软件包的编号。
接下来一行包含1个正整数q,表示询问的总数。
之后q行,每行1个询问。询问分为两种:
installx:表示安装软件包x
uninstallx:表示卸载软件包x
你需要维护每个软件包的安装状态,一开始所有的软件包都处于未安装状态。对于每个操作,你需要输出这步操作会改变多少个软件包的安装状态,
随后应用这个操作(即改变你维护的安装状态)。
输出文件包括q行。
输出文件的第i行输出1个整数,为第i步操作中改变安装状态的软件包数。
参考AC代码
|
|
思路
本题有2个操作:
初始时树上所有结点权值均为0;
安装操作将根到x结点的所有结点权值置为1,卸载操作将x结点的子树中所有结点权值置为0
前者显然树剖 后者显然dfs序 dfs序可在dfs2中求出
lazy用来表示是否安装的标记
由于结点编号从0开始 全部++方便计算
由于是区间求和问题 sum需要加上每一个点的值
solve1解决树剖更新 用总结点减去1的个数=0的个数 即为答案
solve2解决子树 query的1个数就是答案 注意2个操作后都有更新操作