树链剖分

树链剖分学习笔记

﹥>﹥吖頭↗ 提交于 2019-11-29 12:14:55
因为抄模板抄错(...),来来回回看了很多遍板子,debug了好久。感觉理解得很到位了。 洛谷P3384树链剖分 #include<bits/stdc++.h> #define debug printf("!"); using namespace std; typedef long long ll; const int maxn=2e5+50; vector<int>p[maxn]; int val[maxn],mod; int siz[maxn],dep[maxn],fad[maxn],son[maxn]; int top[maxn],tid[maxn],rnk[maxn],cnt; /* siz[i] 保存以i为根的树的大小 dep[i] i节点的深度 fad[i] i的父亲 son[i] i的重儿子 top[i] 重链的顶端节点 tid[i] i的新编号(dfs) rnk[i] rnk[tid[i]]=i */ inline void dfs1(int u,int fa,int d) { /* u 当前节点 fa 父亲 d 深度 */ dep[u]=d; fad[u]=fa; siz[u]=1; for(int i=0;i<p[u].size();i++) { int v=p[u][i]; if(v==fa)continue; dfs1(v,u,d+1); siz[u]+

P4592 [TJOI2018]异或 树链剖分 01trie

时光怂恿深爱的人放手 提交于 2019-11-29 11:37:22
树剖上维护可持久化01trie即可 #include<bits/stdc++.h> using namespace std; #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);--i) #define ll long long #define see(x) (cerr<<(#x)<<'='<<(x)<<endl) #define inf 0x3f3f3f3f #define CLR(A,v) memset(A,v,sizeof A) ////////////////////////////////// const int N=2e6+10; int T[N],Siz[N<<5],t[N<<5][2],ncnt; void upnode(int k,int val,int pre,int &pos) { pos=++ncnt; Siz[pos]=Siz[pre]+1; t[pos][0]=t[pre][0]; t[pos][1]=t[pre][1]; if(k<0)return ; int c=(val>>k)&1; upnode(k-1,val,t[pre][c],t[pos][c]); } int qmax(int k,int val,int pre,int

[树链剖分]BZOJ3589动态树

允我心安 提交于 2019-11-29 10:08:09
题目描述 别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件 事件0: 这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子. 事件1: 小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次. 输入 第一行一个整数n(1<=n<=200,000), 即节点数. 接下来n-1行, 每行两个数字u, v. 表示果子u和果子v之间有一条直接的边. 节点从1开始编号. 在接下来一个整数nQ(1<=nQ<=200,000), 表示事件. 最后nQ行, 每行开头要么是0, 要么是1. 如果是0, 表示这个事件是事件0. 这行接下来的2个整数u, delta表示以u为根的子树中的每个节点长出了delta个果子. 如果是1, 表示这个事件是事件1. 这行接下来一个整数K(1<=K<=5), 表示这次询问涉及K个树枝. 接下来K对整数u_k, v_k, 每个树枝从节点u_k到节点v_k. 由于果子数可能非常多, 请输出这个数模2^31的结果. 输出 对于每个事件1, 输出询问的果子数. 样例输入 5 1 2 2 3 2 4 1 5 3 0 1 1 0 2 3 1 2 3 1 1 4 样例输出

树链剖分入门

荒凉一梦 提交于 2019-11-29 06:16:35
树链剖分主要解决在树上某条路径上或某棵子树的sum与最值 入门树链剖分,最重要的概念是重儿子,用son[]记录。son[i]代表的是以i为根,节点最多子树根的编号。通过son,我们将树中的边分为两种,轻边和重边。重边是每一个点与它重儿子的连边。将链续的重边串起来,构成了一条条重链。对于每个点,它一定在某条重链上,特殊的,一个单独的点也可以是一条重链。对于一条重链上的点,他们的dfs序是连续的,对与一颗子树上的点,他们的dfs序也是连续的,于是我们将树上的点转化成一个区间,在区间用线段树上求解或修改。 树链剖分的核心便是如何将树剖分成若干链。 在此之前,先了解以下七个参数的意义。 fa[x] x的父亲节点编号 dep[x] x的深度 size[x] 以x为根子树的节点,用来求son seg[x] 以son为基础的dfs2序,即其在线段树上的编号 rev[x] 用来将线段树上的编号转化为原编号。 son[x] 记录重儿子   top[x] 记录x所在重链dep最小的点 树链剖分需要的前置知识有DFS+LCA+线段树 我们在两次dfs中求出上述七个参数 dfs1:求出dep,f,size,son。 inline void dfs1(int u,int f){ int i,v; size[u]=1; fa[u]=f; dep[u]=dep[f]+1; for(i=fir[u];v=to[i

洛谷P3398 树链剖分求树上的两条路径是否有交点

删除回忆录丶 提交于 2019-11-28 20:36:56
题目链接: https://www.luogu.org/problem/P3398 给出a,b,c,d,问a到b的路径和c到d的路径是否有交点。 这里有一个规律(看题解才知道的)就是如果两条路径有交点,那么LCA(a,b)在c到d的路径上或者LCA(c,d)在a到b的路径上,所以我们先求出ab=LCA(a,b),cd=LCA(c,d),判断cd是否在a到b的路径上,只要满足dis(cd,a)+dis(cd,b)==dis(a,b)就行了,也就是cd到a的距离加上cd到b的距离等于a到b的距离,注意这里是距离,用树链剖分的话要用点来代替边,可能有其他做法也不一定。 代码: #include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<map> #include<stack> #include<cmath> #include<vector> #include<set> #include<cstdio> #include<string> #include<deque> using namespace std; typedef long long LL; #define eps 1e-8 #define INF 0x3f3f3f3f #define maxn 200005 struct

树链剖分 入门

六月ゝ 毕业季﹏ 提交于 2019-11-28 17:47:22
什么是树链剖分?   树链剖分说白了就是将树的节点按照某种顺序编号,使其在特殊的链上编号连续(类似区间),方便用数据结构维护。 如何树链剖分?   树链剖分一般分为重链剖分和长链剖分,这里只介绍重链剖分( 我也只会重链剖分 )。   重链剖分中有几个概念: 重儿子:一个节点的所有子节点中,以某个子节点为根的子树中节点数量最多的称为重儿子(如果最多的数量相同随便取一个)。 轻儿子:一个节点的所有子节点中,不是重儿子的节点就是轻儿子(这不是废话吗)。 重边:父节点与重儿子所连成的边。 轻边:父节点与轻儿子所连成的边。 重链:重边所连成的链。 轻链:轻边所连城的链。 如图所示,蓝色的边为重边,绿色的节点为重儿子。 树链剖分的第一步就是一遍dfs找出所有节点的重儿子,并处理出深度与父子关系(以后有用)。 代码如下: void dfs(int x){ size[x]=1,d[x]=d[fa[x]];//size表示节点数量,d表示深度 for(int i=head[x];i;i=nxt[i]){ int y=ver[i]; if(y==fa[x]) continue;//fa表父节点 dfs(y); size[x]+=size[y]; if(size[y]>size[son[x]]) son[x]=y;//son表示重儿子 } } 树链剖分的第二步便是按照 重儿子先遍历,轻儿子后遍历

【模板】树链剖分(Luogu P3384)

佐手、 提交于 2019-11-28 10:31:56
题目描述 众所周知 树链剖分是个好东西QWQ 也是 一个代码量破百的算法 基本定义 树路径信息维护算法。 ž将一棵树划分成若干条链,用数据结构去维护每条链,复杂度为O(logN)。 其实本质是一些数据结构/算法在树上的推广 华丽的分割线----------------------------------------------------------------------------------------------------------- 具体步骤 预处理 第一遍dfs求出树每个结点的深度deep[x],其为根的子树大小size[x] 以及祖先的信息fa[x][i]表示x往上距离为2^i的祖先 第二遍dfs ž根节点为起点,向下拓展构建重链 选择最大的一个子树的根继承当前重链 其余节点,都以该节点为起点向下重新拉一条重链 ž给每个结点分配一个位置编号,每条重链就相当于一段区间,用数据结构去维护。 把所有的重链首尾相接,放到同一个数据结构上,然后维护这一个整体即可 修改操作 ž1、单独修改一个点的权值 根据其编号直接在数据结构中修改就行了。 2、修改点u和点v的路径上的权值 (1)若u和v在同一条重链上 直接用数据结构修改pos[u]至pos[v]间的值。 (2)若u和v不在同一条重链上 一边进行修改,一边将u和v往同一条重链上靠,然后就变成了情况(1)。 具体代码

树链剖分入门博客推荐

老子叫甜甜 提交于 2019-11-28 08:23:30
树链剖分入门博客推荐 网上关于树链剖分的文章很多,自己在学习树链剖分的时候找到了几篇好的文章,代码规范,有图例。树链剖分的部分自己先挖个坑,以后再专门写个博客来总结。 博客推荐 博客一 推荐理由:每个函数完成后的结果都有相应的图例,可以自己模拟一下和图对应,加深对函数功能的理解。 博客二 推荐理由:完整的函数实现,树链剖分+线段树的完整代码。 博客三 推荐理由:代码规范,通俗易懂。 来源: https://www.cnblogs.com/alking1001/p/11402131.html

树链剖分模板

心已入冬 提交于 2019-11-28 06:31:31
树链剖分模板 如题,已知一棵包含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为根节点的子树内所有节点值之和 #include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #define LL long long using namespace std; const int MAXN = 2e5+10;//点的个数 struct node{ int l,r; int sum,laze;//线段树 }tree[MAXN<<2]; struct edge{ int next,to; }e[MAXN<<1]; int a[MAXN]; int head[MAXN]; int siz[MAXN];//子树的大小 int top[MAXN];//重链的顶端 int son[MAXN];//每个节点的重儿子 int d[MAXN];//每个节点的深度 int fa[MAXN];//每个节点的父亲节点 int

树链剖分求lca

柔情痞子 提交于 2019-11-28 06:10:52
void dfs1(int u,int f) { fa[u]=f,siz[u]=1,dep[u]=dep[f]+1; int maxson = -1; for(int i=Head[u];~i;i=Edge[i].next) { int &v = Edge[i].to; if(v==f) continue; dfs1(v,u); siz[u]+=siz[v]; if(siz[v]>maxson) maxson=siz[v],son[u]=v; } } int top[maxn]; void dfs2(int u,int t) { top[u]=t; if(!son[u]) return; dfs2(son[u],t); for(int i=Head[u];~i;i=Edge[i].next) { int &v = Edge[i].to; if(v==fa[u]||v==son[u]) continue; dfs2(v,v); } } int main() { memset(Head,-1,sizeof(Head)); int n,m,s; read(n,m,s); while(--n) { int u,v; read(u,v); AddEdge(u,v); } dfs1(s,s); dfs2(s,s); while(m--) { int u,v; read(u,v); while