树链剖分

【树链剖分】洛谷P3379 树链剖分求LCA

自古美人都是妖i 提交于 2019-12-04 21:25:14
其实就是让两个点,看谁的深度大,谁就先跳。直到两个点的top顶点一致时,此时谁的深度小谁就是Lca. zz:https://www.cnblogs.com/rir1715/p/7668338.html 树剖就是把树剖分成若干条不相交的链,目前常用做法是剖成轻重链 所以我们定义siz[x]为以x为根结点的子树的结点个数 对于每个结点x,在它的所有子结点中寻找一个结点y 使得对于y的兄弟节点z,都有siz[y]≥siz[z] 此时x就有一条重边连向y,有若干条轻边连向他的其他子结点【比如z】 这样的话,树上的不在重链上的边的数量就会大大减少 然后我们每次求LCA(x,y)的时候就可以判断两点是否在同一链上 如果两点在同一条链上我们只要找到这两点中深度较小的点输出就行了 如果两点不在同一条链上 那就找到深度较大的点令它等于它所在的重链链端的父节点即为x=f[top[x]] 直到两点到达同一条链上,输出两点中深度较小的点。 //by 减维 #include<cstdio> #include<iostream> using namespace std; struct edge{ int to,ne; }e[1000005]; int n,m,s,ecnt,head[500005],dep[500005],siz[500005],son[500005],top[500005],f[500005

如何写树链剖分

試著忘記壹切 提交于 2019-12-04 11:06:41
树链剖分模板 树链剖分可以把树上的点划分成一条条 连续 的“链”,“链”是一条简单路径,上面的每个点满足祖先后代关系,从根节点到每个点都只需经过最多 \(\log_2n\) 条链。 链上的每一个点 dfs序也是连续的 , 故可以配合其他数据结构解决很多树上查询问题(近乎所有静态树问题) 先定义 siz[u] 为点 u 的子树大小。 int siz[MAXN]; //每个点的子树大小 int dep[MAXN]; //每个点的深度 int mxson[MAXN];//每个点的重儿子 链分为 重链 和 轻链 。 考虑每个点,它只会和它的一个儿子组成链,这个儿子叫做 重儿子 , 其他儿子叫做 轻儿子 。 重儿子满足: siz[mxson[u]] 最大. 第一次dfs: 任务清单: 求出每个节点 u 的 siz[u] , dep[u] , fa[u] 求出每个节点的 重儿子 (注意叶节点没有重儿子!) void dfs1(int u, int faa){ dep[u] = dep[faa] + 1; siz[u] = 1; fa[u] = faa; int mxsiz = 0; for(int e = h[u]; e; e = nxt[e]){ int v = ev[e]; if(v == faa) continue; dfs1(v, u); if(mxsiz < siz[v]) {

树链剖分不详细讲解

旧街凉风 提交于 2019-12-04 10:26:27
前置技能:线段树、DFS 当我第一次听到 “树链剖分” 这个算法的时候,感觉它一定很高大上。现在看来,它确实很高大上,不过也十分的 暴力 (个人认为,不喜勿喷) 基本概念  树链剖分,计算机术语,指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结构(树状数组、SBT、SPLAY、线段树等)来维护每一条链。  ————某度百科   百度百科对什么是树剖已经说的很明白了,接下来我们再了解一下其他的概念。 重儿子:对于每一个非叶子节点,它的儿子中子树节点最多的儿子 轻儿子:对于每一个非叶子节点,它的除重儿子以外的儿子 重边:父亲节点连向重儿子的边 轻边:父亲节点连向轻儿子的边 重链:由多条重边连成的一条树链 轻链:由多条轻边连成的一条树链 在这张图片中,带红点的就是轻儿子,其余为重儿子;加粗的边为重边,其余的为轻边; \(1 -> 14, \; 2 -> 11, \; 3 -> 7\) 的路径为重链,其余的为轻链。 前面某度已经说了,树链剖分要通过轻重边剖分将树分为多条链,那么它是怎么找出轻重边,又是怎么剖分的的呢?不要着急,我们接着讲 实现方法 先来说一说我们需要求哪些东西 变量 含义 \(f[i]\) 结点 \(i\) 的父亲 \(son[i]\) 结点 \(i\) 的重儿子(如果有 \(i\) 有两个及以上的重儿子

【数据结构】——树链剖分

家住魔仙堡 提交于 2019-12-04 01:24:14
树链剖分——简单而强大的数据维护方法 只是放个板子而已。 用我的码风覆盖了的。 1 #include<bits/stdc++.h> 2 using namespace std; 3 //------------------------------------------------------ 4 inline int read(){ 5 int f=1,x=0; 6 char c=getchar(); 7 while(!isdigit(c)){ 8 if(c=='-') f=-1; 9 c=getchar(); 10 } 11 while(isdigit(c)){ 12 x=x*10+c-'0'; 13 c=getchar(); 14 } 15 return x*f; 16 } 17 //------------------------------------------------------ 18 const int N=2e5+10; 19 int at[N<<1],sum[N<<1]; 20 int head[N],cnt,n,m,r,mod,tot,ans; 21 int w[N],dep[N],siz[N],fa[N],son[N],top[N],id[N],w2[N]; 22 struct edge{ int to,next; }e[N<<1]; 23 inline

树链剖分(树剖)

北战南征 提交于 2019-12-04 01:00:46
毒瘤东西…然而某已逝联赛居然历年来搞了三道左右的树剖…唉~ 问题类型 顾名思义属于图论的东西。基于数据结构: 线段树 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和 操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z 操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和 原理方法 那…我觉得我自己来描述也没什么好说的,不如搬来我觉得最好理解的博客吧。 洛谷日报:树链剖分 前置知识点应该加上一个 LCA ,但是树上差分似乎用处不大。 讲的很清晰,但是至少对于蒟蒻的我来说,他的板子代码确实让人难受的很 所以这里附上一份板子题链接与其代码(代码摘自zengqinyi from luogu) 板子题 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #define Rint register int #define mem(a,b) memset(a,(b),sizeof(a)) #define Temp template<typename T> using namespace std; typedef

luogu_P3384 【模板】树链剖分

 ̄綄美尐妖づ 提交于 2019-12-03 21:01:18
树链剖分,差不多就是树上分块 核心思想:利用dfs序的连续性,把链和子树套在线段树上做 代码略长,记得随时取模 #include<iostream> #include<cstdio> #define ri register int #define u int namespace opt { inline u in() { u x(0),f(1); char s(getchar()); while(s<'0'||s>'9') { if(s=='-') f=-1; s=getchar(); } while(s>='0'&&s<='9') { x=(x<<1)+(x<<3)+s-'0'; s=getchar(); } return x*f; } } using opt::in; #define NN 500005 #define MM 500005 namespace tu { u N,M,R,P;// u v[NN];//原点权值 u w[NN];//dfsz序权值 u num;//num,dfs序 u cnt,h[NN];//星 u to[NN];//原点->dfs序 struct node { u to,next; } a[MM<<1]; //星 struct nods { u siz,fa,dep,son,top;//大小,父亲,深度,重儿子 ,链顶 } poi[NN]; /

【Luogu P3384】树链剖分模板

大憨熊 提交于 2019-12-03 14:07:26
树链剖分的基本思想是把一棵树剖分成若干条链,再利用线段树等数据结构维护相关数据,可以非常 暴力 优雅地解决很多问题。 树链剖分中的几个基本概念: 重儿子:对于当前节点的所有儿子中,子树大小最大的一个儿子就是重儿子(子树大小相同的则随意取一个) 轻儿子:不是重儿子就是轻儿子 重边:连接父节点和重儿子的边 轻边:连接父节点和轻儿子的边 重链:相邻重边相连形成的链 值得注意的还有以下几点: 叶子节点没有重儿子也没有轻儿子; 对于每一条重链,其起点必然是轻儿子; 单独一个轻叶子节点也是一条重链; 结合上面三条可以得出树剖的一个性质: 重链必然可以囊括所有的节点。 (图片来源百度图片,侵删) 红点标记的是轻儿子,粗线就是重链。结合图片理解概念。 树链剖分需要怎么做呢? 1、用DFS给每一个节点标记深度,父节点和重儿子。 2、用DFS按照DFS遍历的顺序给每一个节点标记新的编号。 关键点:先处理重儿子再处理轻儿子 解释:先处理重儿子可以让重链上的每一个点的编号连续。可以观察上图,线上的数字就是DFS的顺序。使编号连续后,我们就可以使用线段树来维护数据了。 做完以上两步就算是完成了树链剖分了,接下来要做的就是利用其它数据结构来进行维护了。 void add(ll sta,ll to) { edge[++cnt].to=to; edge[cnt].next=head[sta]; head[sta]

树链剖分与倍增求LCA

风格不统一 提交于 2019-12-03 11:57:45
树链剖分与倍增求 \(LCA\) 首先我要吐槽机房的辣基供电情况,我之前写了一上午,马上就要完成的时候突然停电,然后 \(GG\) 成了送链剖分 其次,我没歧视 \(tarjan LCA\) 1.倍增求 \(LCA\) 理解较为简单的一种方法,但速度略慢 倍增是啥? 每个数字都可以拆成几个二的整数次的和,我们可以找出每个数字是由哪几个二的整数次的数合成的 比如说 \(14 _ {10} = 1110_2 = 1000 _2 + 100 _2 + 10 _2 = 8 _ {10} + 4 _{10} + 2 _{10}\) 那么我们如果要统计一段长度为十四的区间的最小值,我们就可以先统计前八个数的最小值,再统计之后的四个,再统计之后的两个。 我们可以用 \(f[i][j]\) 表示从 \(i\) 开始往后 \(2^j\) 长度的最小值 给宁康康代码 for( int i = 1; i <= 23; i++ ){ for( rint j = 1; j <= n; j++ ){ //a[i][j]存的是i往后2的j次长度的区间的右节点是哪儿 f[i][j] = min( f[i][j - 1], f[a[i][j - 1]][j - 1] ); } } 下面这个东西是啥意思呢 f[i][j - 1], f[a[i][j - 1]][j - 1] \(2^j = 2^{j-1} +2^{j

[总结]树链剖分的详细介绍

╄→尐↘猪︶ㄣ 提交于 2019-12-03 11:11:42
目录 一、关于树链剖分 二、树链剖分实现流程 二、树链剖分具体实现 1.需要表示的变量 2.储存一棵树 3.第一次遍历,处理fa,dep,size,son数组 4.第二次遍历,处理top,seg,rev数组 5.初始化线段树 6.单点修改 7.区间修改---以x为根结点的子树内节点的值都加val 8.区间修改---节点x到节点y的最短路径中同时加val 9.区间查询---以x为根结点的子树内节点的值的和 10.区间查询---节点x到节点y的最短路径中节点的和 11.区间查询---节点x到节点y的最短路径中的最大值/最小值 三、例题 例1: P3384 【模板】树链剖分 一、关于树链剖分 你的好盆友最近抛给你这样一个难题 (无中生友) : " 一棵树由n个节点,每个节点都有一个权值w,现在想让你对这棵树完成下列操作: 1.把节点u的权值改为t 2.询问节点u到节点v的权值和 3.节点u到v的最大值 " 你看了看题目,发现这就是树链剖分的板子题... 好吧,那如果你不会树链剖分呢? ... 于是你的朋友告诉你这是树链剖分,并因为你不会树链剖分把你嘲讽了 (开玩笑而已啦) ... 只观察这个问题的三个操作,你惊讶的发现这是线段树所擅长的事情,即单点修改,区间查询。 实际上,如果这棵树退化成一条链,那么你完全可以用线段树来解决这个问题。 你思考了一下,得出了树链剖分是什么东西: 树链剖分

树链剖分学习笔记

徘徊边缘 提交于 2019-12-03 10:10:29
树链剖分 概述 :通过将一棵树上的点分为轻重链,来降低复杂度,此时lca查询复杂度为 \(O(logn)\) ,支持在线。 前置 重儿子 :一个有根树的一个点 子树最大的儿子 轻儿子 :其它的儿子 重链 :由重儿子连接成的链 轻链 :其它的所有链 下图是一棵剖好的树 图片来自于[ 知识点]树链剖分 树剖 树剖本体其实只有两个dfs 第一个dfs处理每个子树的大小,重儿子一类的信息 第二个dfs处理剖出的链的信息 void dfs1(int x) { int mx = -1; for(int i = head[x]; i; i = e[i].next) { int v = e[i].v; if(v == fa[x]) continue; dep[v] = dep[x] + 1;//处理深度 fa[v] = x;//父节点 siz[x]++;//大小 dfs1(v); siz[x] += siz[v];//回溯 if(siz[v] > mx) {//保留重儿子 mx = siz[v]; son[x] = v; } } } void dfs2(int x, int tp) { top[x] = tp;//链顶 if(son[x] != 0) { dfs2(son[x], tp);//优先搜索重儿子,让重儿子先成链 } for(int i = head[x]; i; i = e[i]