树链剖分

【模板】重链剖分(树链剖分)

一曲冷凌霜 提交于 2020-02-02 09:48:26
我们知道对一列数进行区间或单点加减,乘除和区间求值等操作可以用线段树或树状数组 那么,如何对带权树上一条路径中的数进行这样的操作呢? 此时就用到了线段树的树上版——树链剖分 树链剖分的目的在于把树变成一条线段,以方便区间操作 显然,我们无法让每一条树上路径中所有数在这条线段中相邻,但又不能让它们太分散 于是,我们想寻找一个使区间操作均摊复杂度较小的树-线段映射方法 介绍概念: 重儿子:父亲节点的所有儿子中子树结点数目最多(size最大)的结点; 轻儿子:父亲节点中除了重儿子以外的儿子; 重边:父亲结点和重儿子连成的边; 轻边:父亲节点和轻儿子连成的边; 重链:由多条重边连接而成的路径; 轻链:由多条轻边连接而成的路径; 介绍数组: fa[i]:点i的父亲 top[i]:点i所在重链的顶点 si[i]:以点i为根的子树的节点数 son[i]:点i的重儿子 dfn[i]:点i的dfs序 dep[i]:点i的深度 比如上面这幅图中,用黑线连接的结点都是重结点,其余均是轻结点, 2-11就是重链,2-5就是轻链,用红点标记的就是该结点所在重链的起点,也就是下文提到的top结点, 还有每条边的值其实是进行dfs时的执行序号。 (图和说明来自 这位大佬的博客 ) 遍历时,我们使用先序遍历,并先遍历重儿子,再遍历轻儿子 于是,dfn重儿子=dfn父亲+1,即一条重链上从上到下dfn相邻且依次增大

SPOJ QTREE 树链剖分

旧城冷巷雨未停 提交于 2020-01-27 11:37:41
A - Query on a tree Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/contest/view.action?cid=87378#problem/A Description You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3... N -1. We will ask you to perfrom some instructions of the following form: CHANGE i ti : change the cost of the i-th edge to ti or QUERY a b : ask for the maximum edge cost on the path from node a to node b Input The first line of input contains an integer t , the number of test cases ( t <= 20). t test cases follow. For each test case: In the

树链剖分

淺唱寂寞╮ 提交于 2020-01-26 17:08:07
定义:树链(树上的路径),剖分(把路径分成重链和轻链); 用途:在一棵树上进行修改、求极值、求和。 时间复杂度: ,数据范围100,000; 概念: 重儿子:节点u所有的儿子中,siz最大的儿子; 轻儿子:节点u所有的非重儿子的儿子; 重链:所有连续重儿子连接成的链; 轻链(轻边):由节点u跟轻儿子连接的边,最多包含两个定点。 结论: 所有的重链个数不超过 . 所有的轻边的个数不超过 ; 轻边的size*2<SIZE; 维护的数据 val[u] 节点u的价值 tid[u] 节点u的新编号,用于维护线段树 valt[u] 节点u的新编号对应的价值,用于维护线段树 size[u] 以u为根的子树的节点数; 用于区分重儿子和轻儿子。 dep[u] 节点v的深度,根节点的深度为1或0; top[u] 节点u所在链的顶端节点,(u,v)如果为重链,top[u]==top[v],轻链或者轻链重链的结合,top[u]!=top[v]. fa[u] 表示u节点的额父亲节点 实现过程: 邻接表存树 DFS1,维护出fa[],dep[]和siz[] DFS2,优先访问重儿子,维护出tid[],valt[],top[] 用tid[]和valt[]建立线段树。 修改,修改u的tid[u]在线段树上的值即可。 查询,两部查询,把查询的链分成重链和轻链查询。如果是复合链,优先查询dep[top[u]

树链剖分

本秂侑毒 提交于 2020-01-26 16:41:14
题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和 操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z 操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和 输入 第一行包含4个正整数N、M、R、P,分别表示树的结点个数、操作个数、根节点序号和取模数(即所有的输出结果均对此取模)。 接下来一行包含N个非负整数,分别依次表示各个节点上初始的数值。 接下来N-1行每行包含两个整数x、y,表示点x和点y之间连有一条边(保证无环且连通) 接下来M行每行包含若干个正整数,每行表示一个操作,格式如下: 操作1: 1 x y z 操作2: 2 x y 操作3: 3 x z 操作4: 4 x 输出 输出包含若干行,分别依次表示每个操作2或操作4所得的结果(对P取模) 样例输入 5 5 2 24 7 3 7 8 0 1 2 1 5 3 1 4 1 3 4 2 3 2 2 4 5 1 5 1 3 2 1 3 样例输出 2 21 提示 对于100%的数据:N<=100000,M<=100000 输入的每一个数均小于2147483647 重儿子:siz[u

[树链剖分]JZOJ 2677 树A

微笑、不失礼 提交于 2020-01-26 06:21:56
Description 已知无向连通图 G 由 N 个点, N-1 条边组成。每个点有给定权值。现有 M 个操作,操作分为 2 种:操作 1 ,将某点权值更改;操作 2 ,询问从点 A 至点 B 路径上所有点的权值和。 Input 每个输入文件中仅包含一个测试数据。 第一行包含两个整数 N,M 。 第二行至第 N 行每行包含 2 个整数 ,A 、 B ,表示节点 A 与节点 B 有一条边相连。 第 N+1 行包含 N 个整数,表示第 N 个点的初始权值。 第 N+2 行至第 N+M+1 行每行包含三个整数, K 、 A 、 B (若 K=1 ,表示将点 A 权值改为 B ;若 K=2 表示询问点 A 至点 B 路径上所有点的权值和) Output 输出文件若干行,分别对应每次操作 2 的答案。 Sample Input 3 2 1 2 1 3 1 2 3 2 1 3 2 2 3 Sample Output 4 6 Data Constraint Hint 对于 60% 的数据, 1<=N,M<=1000 对于 100% 的数据, 2<=N<=30000;0<=M<=200000 分析 树剖模板…… #include <iostream> #include <cstdio> #define lson (x<<1) #define rson ((x<<1)+1) using

树链剖分

故事扮演 提交于 2020-01-23 00:23:19
转载请标明出处,以下部分内容部分引自 Ivanovcraft 巨佬的博客 ,加上了一些自己的见解和自己的代码。 对于修改树上的点权值,我们可以想到用树上差分来做。 对于求两点之间路径上的点的权值和,我们可以利用倍增的思想很好的解决这个问题。 可是,当修改与查询结合起来,就不能把这两种方法简单结合起来了。(这样的话复杂度会很劣) 于是乎,就要引出我们今天的主角了——— 树链剖分 定义 树链剖分,计算机术语,指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结构(树状数组、BST、SPLAY、线段树等)来维护每一条链。(来自百度百科) 树链剖分 让我们先来认识几个概念: 重儿子:父亲节点的儿子中子树最大(节点数最多)的儿子; 轻儿子:父亲节点的儿子中除重儿子以外的其他儿子; 重边:连接父节点和重儿子的边; 轻边:连接父节点和亲儿子的边; 重链:由一条或多条重边连接形成的路径; 轻链:由一条或多条轻边连接形成的路径。 P.S.:若父节点中存在多个节点数最多的儿子,就随便取其中一个作为它的重儿子。 认识完这些概念,再让我们看看树链剖分的原理吧! 原理 先放一张图片(以下图片来自百度百科) 呐,这就是一个树链剖分的标准图了。图中标粗的都是重边,其余的边都是轻边。 其中,用红点标记的是指该节点是当前节点所在重链的顶端。

树链剖分

风流意气都作罢 提交于 2020-01-22 20:25:17
重链剖分 概念: 重儿子:父亲结点的所有儿子中子树结点数目最多( \(size\) 最大)的结点 轻儿子:父亲结点中除了重儿子以外的儿子 重边:父亲结点和重儿子连成的边 轻边:父亲结点和轻儿子连成的边 重链:由多条重边连接而成的路径 轻链:由多条轻边连接而成的路径 性质: 在轻边 \((u,v)\) 中, \(size(u)/2 \geqslant size(v)\) 从根结点到任意一点的路径上,不超过 \(log\ n\) 条轻链和 \(log\ n\) 条重链 时间复杂度为 \(O(n\ log\ n)\) 先求出每个结点所在的子树大小,找到它的重儿子(即处理 \(siz\) 数组和 \(son\) 数组),记录其父亲以及深度(即处理 \(fa\) 数组和 \(de\) 数组) \(code\) : void dfs_son(int x,int fath) { siz[x]=1; fa[x]=fath; de[x]=de[fath]+1; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fath) continue; dfs_son(y,x); siz[x]+=siz[y]; if(siz[son[x]]<siz[y]) son[x]=y; } } 再将重儿子连接成重链,记录每个点所属重链的顶部端点(即处理 \

Water Tree CodeForces 343D 树链剖分+线段树

北慕城南 提交于 2020-01-20 00:42:07
Water Tree CodeForces 343D 树链剖分+线段树 题意 给定一棵n个n-1条边的树,起初所有节点权值为0。 然后m个操作, 1 x:把x为根的子树的点的权值修改为1; 2 x:把x结点到根路径上的点修改为0; 3 x:查询结点x的值。 解题思路 这个因为是在树上进行的操作,所以首先需要把树进行一些转化,比如使用dfs序列转变成一维的,这样方便使用线段树或则树状数组来进行操作。但是因为这里的操作2需要把x节点和它的父节点赋值为0,所以需要树链剖分来进行处理。 关于树链剖分的讲解可以参照我的代码,如果是初学者的话,我有一篇博文专门总结了一些优秀的关于树链剖分的文章,可以参考, 点我进去 代码实现 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int maxn=5e5+7; struct node{ int l, r; int val, lazy; //val表示在l到r的范围内是不是都有水,lazy就是标记了 }t[maxn<<2]; //线段树的基本单元 struct edge{ int to, next; }e[maxn<<1]; //采用链式向前星的形式来存边 int son[maxn], size

[置顶] 树链剖分小节

自作多情 提交于 2020-01-20 00:38:10
前段时间学习了下树链剖分,好久没看了,今天又复习一遍,赶紧写下来,别又忘了。 我们在信息学竞赛中,有时会碰到这么一类题型,在一棵树中,修改两点之间路径上的所有边(或点)上的某个变量(如边的长度,点的权值等等),然后询问单个点(或边)或者两点之间路径上的所有点(或边)的某些性质(如边权之和,最大边最小边等等)。对于这样的题,往往容易往线段树上去靠,但是,单单是用线段树是无法维护每一条链的性质的,所以我们需要一种算法将树链分开来,使得每条链可以和线段树中的一个区间一一对应上。(当然树链剖分远远不止这些简单的应用,也不一定要和线段树有什么关系,总之就是将树链剖分开来吧)。 树链剖分有很多种剖分方法,最常用的应该就是轻重边剖分了吧(在网上大部分介绍的都是这种剖分方法),什么是轻重边剖分呢? 我们首先将树中的边分为两部分,轻边和重边,记size(U)为以U为根的子树的节点的个数,令V为U的儿子中size最大的一个(如有多个最大,只取一个),则我们说边(U,V)为重边,其余的边为轻边(如下图所示红色为重边,蓝色为轻边)。 我们将一棵树的所有边按上述方法分成轻边和重边后,我们可以得到以下几个性质: 1:若(U,V)为轻边,则size(V)<=size(U)/2。 这是显然的。 2:从根到某一点的路径上轻边的个数不会超过O(logN),(N为节点总数)。 这也是很简单

【模板】树链剖分

百般思念 提交于 2020-01-20 00:36:48
树链剖分: 用于解决一系列维护静态树上信息的问题。这些问题看起来非常像一些区间操作搬到了树上。 (例如:一棵带权树,需要维护修改权值操作以及从$u$到$v$简单路径上的权值和) 树链剖分就是通过某种策略(一般是轻、重边剖分)将原树链划分成若干条链,每条链相当于一个序列,此时就可以用区间数据结构(一般是线段树)维护这些链。 需要维护的值: $f(x)$:$x$在树中的父亲。 $dep(x)$:$x$在树中的深度。 $siz(x)$:$x$的子树大小。 $son(x)$:$u$的重儿子:在$u$的所有儿子中$siz$值最大的儿子,$u\rightarrow v$为重边。 ($u$的轻儿子:在$u$的所有儿子中除了重儿子以外的儿子,$u\rightarrow v$为轻边。) $top(x)$:$x$所在重路径的顶部节点。 $seg(x)$:$x$在线段树中的位置(下标)。 $rnk(x)$:线段树中$x$位置对应的树中节点编号,即有$rnk(seg(x))=x$。 轻重边的一些性质: 1、如果$u\rightarrow v$为轻边,则$siz(v)<=siz(u)/2$。 证明:反证法,若存在$siz(v)>siz(u)/2$且存在$siz(v_0)>siz(v)$,那么$siz(v)+siz(v_0)>siz(u)$,即子节点的$siz$和大于父节点的$siz$。 2、从根到任何点$u