树链剖分

[模板]树链剖分

梦想与她 提交于 2020-01-20 00:34:07
link 树链剖分,感觉是一个很神奇的东西,但是其实并不是那样的 树链剖分其实就是一个线段树 线段树处理的是连续区间,所以当你要加的时候都是连续区间修改 所以可以用轻重链的方式将树分解成为链条,然后用线段树处理 可以很容易看到,为什么用的是dfs但不是用的是bfs呢 因为dfs保持了重链是连续的,所以可以用top[x]记录已x为节点的重链最上方,一个点也包含在重链内 若修改区间为(u,v),但是重链的祖先是一起的,所以当他们的LCA相同时,边break 所以现在u,v是连续的 所以查询(u,v)的简单路径和也就处理了 所以说线段树中可以进行的操作在树上也可以执行了 在处理一个问题 在u的子树上加w 所以修改的区间是u在线段树中的位置$(t)$ 到 $t+size(u)-1$ $size$ 记录以它为根 的子节点个数 $deep(x)$ 深度 $father(x)$ 记录父亲 $son(x)$ 它的重儿子 $top(x)$ 所在重路径的顶部节点 $seg(x)$ x在线段树中的编号 $rev(x)$ 线段树中x的位置所对应的树中节点编号 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int

树链剖分

穿精又带淫゛_ 提交于 2020-01-19 23:49:42
代码转自:https://www.cnblogs.com/George1994/p/7821357.html 首先需要知道树链剖分有什么用 有的类似于线段树的题目 是对树上的某点或点与点之间的路径进行修改,然后查询某点或点与点之间路径的一些性质 像这样的题目虽然类似线段树 但是却无法简单就用线段树来做 因为线段树只能存储节点的一些性质 对于路径就无能为力了 因此我们需要将树 和路径 分开,使得每一条链和线段树的区间对应 就是树链剖分 当然树链剖分也不仅限于与线段树结合 树链剖分很很多种方法,普遍用到的是轻重边剖分 我们首先将树中的边分为两部分,轻边和重边,记size(U)为以U为根的子树的节点的个数,令V为U的儿子中size最大的一个(如有多个最大,只取一个),则我们说边(U,V)为重边,其余的边为轻边(如下图所示红色为重边,蓝色为轻边)。 我们将一棵树的所有边按上述方法分成轻边和重边后,我们可以得到以下几个性质: 1:若(U,V)为轻边,则size(V)<=size(U)/2。 这是显然的。 2:从根到某一点的路径上轻边的个数不会超过O(logN),(N为节点总数)。 这也是很简单,因为假设从跟root到v的路径有k条轻边,它们是 root->...->v1->...->v2->......->vk->...->v,我们设size(v)=num,显然num>=1,则由性质1

【线段树 树链剖分 差分 经典技巧】loj#3046. 「ZJOI2019」语言【未完】

吃可爱长大的小学妹 提交于 2020-01-15 21:36:41
还是来致敬一下那过往吧 题目分析 先丢代码 1 #include<bits/stdc++.h> 2 const int maxn = 100035; 3 const int maxm = 200035; 4 const int maxNode = 20000035; 5 6 struct node 7 { 8 int top,son,fa,tot; 9 }a[maxn]; 10 struct point 11 { 12 int u,v; 13 point(int a=0, int b=0):u(a),v(b) {} 14 }; 15 struct tree 16 { 17 int ls,rs,cov,val; 18 }f[maxNode]; 19 int n,m,tot; 20 long long ans,det; 21 int chain[maxn],chTot,rt[maxn]; 22 int edgeTot,head[maxn],edges[maxm],nxt[maxm],dep[maxn]; 23 std::vector<point> opt[maxn]; 24 std::vector<int> inc[maxn],dec[maxn]; 25 26 int read() 27 { 28 char ch = getchar(); 29 int num = 0, fl = 1;

浅析树链剖分

依然范特西╮ 提交于 2020-01-15 13:50:07
前言 树链剖分 , 我觉得最精妙的地方就在于它是通过$dfs$序将树形结构转为线性结构便于处理,进而可以用数据结构(线段树、树状数组等)去进行修改和查询。 将复杂的结构转化为相对我们熟悉简单的结构,这个思想对很多问题是通吃的,不仅仅在树形问题,算法中,在其他领域中也常常会用到这种思想 我们先来回顾两个问题 : 1.将树从$x$到$y$结点最短路径上所有节点的值都加上z 我们很容易想到,树上差分可以以 $O(n+m)$的优秀复杂度解决这个问题 2.求树从$x$到$y$结点最短路径上所有节点的值之和 $lca$大水题,我们又很容易地想到, $dfs$ $O(n)$预处理每个节点的$dis$(即到根节点的最短路径长度) 然后对于每个询问,求出$x,y$两点的$lca$,利用$lca$的性质$dis(x,y)=dis(x)+dis(y)-2*dis(lca)$求出结果, 时间复杂度 $O(mlogn+n)$ 现在来思考一个$bug$: 如果刚才的两个问题结合起来,成为一道题的两种操作呢? 刚才的方法显然就不够优秀了(每次询问之前要跑$dfs$更新 $dis$ ) 理解 树剖是通过轻重边剖分将树分割成多条链,然后利用数据结构来维护这些链(本质上是一种优化暴力) 给定一棵有根树,对于每个非叶结点$u$,设$u$的子树中结点数最多的子 树的树根为$v$,则标记$(u,v)$为重边,从$u

【模板】树链剖分

丶灬走出姿态 提交于 2020-01-12 20:00:13
惨了惨了我好喜欢这个算法呜呜呜快超过 \(MST\) 了 树剖涉及的知识: \(LCA\) (最近公共祖先/倍增),线段树 已知一棵包含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为根节点的子树内所有节点值之和 (我理解中的)树剖就是将树上的问题,转化为线性来处理。 通过两次搜索,对每个结点加以一些标记,其中还包括对结点进行新的编号 编号之后就满足一个结点为根的子树,它们的新编号是连续的。 于是就可以维护区间和了w!(线段树好啊233) (懒得写太多了) #include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <cmath> using namespace std; #define MAXN 233333 //变量们! int n,m,r,mod; int tot=0,cnt=0; int ans[MAXN<<2],tag[MAXN<<2]; struct qwq {

SPOJ QTREE 树链剖分

微笑、不失礼 提交于 2020-01-05 03:13:01
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

树链剖分

安稳与你 提交于 2019-12-28 17:16:20
0.前置知识 必备:链式前向星,dfs序。 维护:线段树、树状数组、BST。 1.引入 树链剖分,简单来说就是把树分割成链,然后维护每一条链。一般的维护算法有线段树,树状数组和BST。复杂度为 O ( l o g n ) O(log n) O ( l o g n ) 。 2.剖分方式 对于每一个节点,它的子节点中 子树的节点数最大的 为重儿子,连接到重儿子的边称为重边。例如: 加粗节点为重儿子。除重儿子和重边外的节点和边均为轻儿子或轻边。根不是重儿子也不是轻儿子,因为它根本就不是儿子。 以轻儿子或者根为起点的,由重边连接的一条连续的链称为重链。特别地,若一个叶子结点是轻儿子,那么便有一条以该叶子结点为起点的长度为1的重链。上图中, 1 − 3 − 6 − 8 1-3-6-8 1 − 3 − 6 − 8 是一条重链, 7 7 7 一个节点也是一条重链。 3.预处理 树链剖分的预处理本质上就是2个dfs。 第一个dfs 一共完成四项任务。 标记节点深度:dep[] 标记节点的父节点:fa[] 标记节点的子树大小:siz[] 标记节点的重儿子编号:son[] 代码: void dfs_1 ( int u , int f ) { siz [ u ] = 1 ; for ( int i = h [ u ] ; i ; i = e [ i ] . next ) { int v = e [ i

P3384 [模板] 树链剖分

余生长醉 提交于 2019-12-27 06:59:43
#include <bits/stdc++.h> using namespace std; typedef long long ll; int n, m, rt, mod, cnt, tot; int val[100005]; int dep[100005]; int id[100005], ed[100005]; int rk[100005]; int sz[100005]; int fa[100005]; int son[100005]; int top[100005]; ll sum[400005]; ll lz[400005]; struct node { int to, nex; }E[200005]; int head[100005]; void dfs1(int x, int pre, int d) { sz[x] = 1; dep[x] = d; fa[x] = pre; for(int i = head[x]; i; i = E[i].nex) { int v = E[i].to; if(v == pre) continue; dfs1(v, x, d + 1); sz[x] += sz[v]; if(sz[son[x]] < sz[v]) son[x] = v; } } void dfs2(int x, int t) { top[x] = t; id[x] =

P3833 [SHOI2012]魔法树 -树链剖分

断了今生、忘了曾经 提交于 2019-12-13 13:13:40
题目链接:https://www.luogu.com.cn/problem/P3833 题目大意: 模板改一改就行了。 # include <bits/stdc++.h> # define LL long long # define RLL register int using namespace std ; template < typename T > inline void read ( T & x ) { x = 0 ; T w = 1 , ch = getchar ( ) ; while ( ! isdigit ( ch ) && ch != '-' ) ch = getchar ( ) ; if ( ch == '-' ) w = - 1 , ch = getchar ( ) ; while ( isdigit ( ch ) ) x = ( x << 3 ) + ( x << 1 ) + ( ch ^ '0' ) , ch = getchar ( ) ; x = x * w ; } # define mid ((l+r)>>1) # define lson rt<<1,l,mid # define rson rt<<1|1,mid+1,r # define len (r-l+1) const LL maxn = 500000 + 10 ; LL n , m ; /

树链剖分详解(洛谷模板 P3384)

假如想象 提交于 2019-12-04 21:59:13
树链剖分详解(洛谷模板 P3384) 洛谷·[模板]树链剖分 写在前面 首先,在学树链剖分之前最好先把 LCA、树形DP、DFS序 这三个知识点学了 emm还有必备的 链式前向星、线段树 也要先学了。 如果这三个知识点没掌握好的话,树链剖分难以理解也是当然的。 树链剖分 树链剖分 就是对一棵树分成几条链,把树形变为线性,减少处理难度 需要处理的问题: 将树从x到y结点最短路径上所有节点的值都加上z 求树从x到y结点最短路径上所有节点的值之和 将以x为根节点的子树内所有节点值都加上z 求以x为根节点的子树内所有节点值之和 目录: 概念 dfs1() dfs2() 处理问题 对剖过后的树建线段树 概念 重儿子:对于每一个非叶子节点,它的儿子中 以那个儿子为根的子树节点数最大的儿子 为该节点的重儿子 (Ps: 感谢 @shzr 大佬指出我此句话的表达不严谨qwq, 已修改) 轻儿子:对于每一个非叶子节点,它的儿子中 非重儿子 的剩下所有儿子即为轻儿子 叶子节点没有重儿子也没有轻儿子(因为它没有儿子。。) 重边:一个父亲连接他的重儿子的边称为重边 //原写法:连接任意两个重儿子的边叫做重边 轻边:剩下的即为轻边 重链:相邻重边连起来的 连接一条重儿子 的链叫重链 对于叶子节点,若其为轻儿子,则有一条以自己为起点的长度为1的链 每一条重链以轻儿子为起点 dfs1() 这个dfs要处理几件事情