树链剖分讲解&模板

原理&实现

线段树是一种可以快速进行区间修改和查询的数据结构,并且我们已经知道可以通过dfs序加线段树来维护一棵子树的信息,那么,有没有一种方法来维护树上的两个点之间的链的信息的方法呢?当然是有的,这时就要请出树链剖分了。

对于一棵有根树,我们维护两个节点之间的路径信息时,可以想到维护两个点到lca的分别的信息,既然是要维护一条链,那么我们尽可能把树的节点进行重新编号,使其有更多连续的编号的节点形成链,这样我们更容易在线段树里去维护它。

所以,我们需要把树上的链分为重链和轻链,重链是由一系列重儿子相连而成的链,所谓重儿子,就是一个节点的儿子中子树大小最大的儿子,这样可以控制不超过logn条链(证明可以自己想想,并不难想),而我们在线段树里维护的复杂度为logn,所以树链剖分的时间复杂度为O(lognlogn)。

下面我们来讲讲具体实现:

fa 父亲节点的编号

top 所在重链的顶端节点的编号

hv 重儿子的编号

sz 子树大小

dep 当前节点深度

w 当前节点在树链剖分中的新编号

首先,我们需要用两个dfs处理出以上信息,我们用第一个dfs处理出fa,hv,sz,dep这几个信息,我们用第二个dfs求出w,top,顺便把所有的节点信息插入线段树

操作时,我们只需要顺着两个节点一直向上找到lca,所以我们可以利用之前处理出的top信息,不断将top的深度更大的节点向top跳,在跳的过程中修改或者查询,注意相遇时的特判!

模板代码

(太丑了不要嫌弃qwq)

Luogu P3384

  1 #include<bits/stdc++.h>
  2 #define maxn 100005
  3 using namespace std;
  4 int n,m,r,p,cnt=0,an=0,num[maxn],fa[maxn],top[maxn],hv[maxn],sz[maxn],w[maxn],dep[maxn],sum[maxn*7],lazy[maxn*7];
  5 vector<int>e[maxn];
  6 inline void read(int &x){
  7     x=0; register char ch=getchar();
  8     while(ch<‘0‘||ch>‘9‘)ch=getchar();
  9     while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar();
 10 }
 11 inline void push(int ID,int nl,int nr){
 12     int m=(nl+nr)>>1;
 13     sum[ID<<1]+=lazy[ID]*(m-nl+1),sum[ID<<1|1]+=lazy[ID]*(nr-m);
 14     lazy[ID<<1]+=lazy[ID],lazy[ID<<1|1]+=lazy[ID],lazy[ID]=0;
 15     sum[ID<<1]%=p,sum[ID<<1|1]%=p,lazy[ID<<1]%=p,lazy[ID<<1|1]%=p;
 16 }
 17 inline void ins(int ID,int nl,int nr,int L,int R,int v){
 18     if(L>R)swap(L,R);
 19     if(nl>=L&&nr<=R){
 20         sum[ID]+=(nr-nl+1)*v,lazy[ID]+=v,sum[ID]%=p,lazy[ID]%=p;
 21         return;
 22     }
 23     int m=(nl+nr)>>1;
 24     if(lazy[ID])push(ID,nl,nr);
 25     if(m>=L)ins(ID<<1,nl,m,L,R,v);
 26     if(m<R)ins(ID<<1|1,m+1,nr,L,R,v);
 27     sum[ID]=(sum[ID<<1]+sum[ID<<1|1])%p;
 28 }
 29 inline int query(int ID,int nl,int nr,int L,int R){
 30     if(L>R)swap(L,R);
 31     if(lazy[ID])push(ID,nl,nr);
 32     if(nl>=L&&nr<=R)return sum[ID];
 33     int ans=0,m=(nl+nr)>>1;
 34     if(m>=L)ans+=query(ID<<1,nl,m,L,R);
 35     if(m<R)ans+=query(ID<<1|1,m+1,nr,L,R);
 36     sum[ID]=(sum[ID<<1]+sum[ID<<1|1])%p;
 37     return ans;
 38 }
 39 inline void dfs1(int x){
 40     sz[x]=1;
 41     for(register int i=0;i<e[x].size();i++){
 42         if(e[x][i]!=fa[x]){
 43             fa[e[x][i]]=x,dep[e[x][i]]=dep[x]+1;
 44             dfs1(e[x][i]);
 45             sz[x]+=sz[e[x][i]];
 46             if(sz[e[x][i]]>sz[hv[x]])hv[x]=e[x][i];
 47         }
 48     }
 49 }
 50 inline void dfs2(int x){
 51     w[x]=++cnt;
 52     ins(1,1,n,cnt,cnt,num[x]);
 53     if(hv[x])top[hv[x]]=top[x],dfs2(hv[x]);
 54     for(register int i=0;i<e[x].size();i++){
 55         if(e[x][i]!=fa[x]&&e[x][i]!=hv[x])top[e[x][i]]=e[x][i],dfs2(e[x][i]);
 56     }
 57 }
 58 inline void o1(int f,int t,int v){
 59     if(dep[top[f]]<dep[top[t]])swap(f,t);
 60     if(top[f]!=top[t]){
 61         ins(1,1,n,w[top[f]],w[f],v);
 62         o1(fa[top[f]],t,v);
 63     }
 64     else ins(1,1,n,w[f],w[t],v);
 65 }
 66 inline void o2(int f,int t){
 67     if(dep[top[f]]<dep[top[t]])swap(f,t);
 68     if(top[f]!=top[t]){
 69         an+=query(1,1,n,w[top[f]],w[f]),an%=p;
 70         o2(fa[top[f]],t);
 71     }
 72     else {
 73         an+=query(1,1,n,w[f],w[t]),an%=p;
 74         printf("%d\n",an);
 75         an=0;
 76     }
 77 }
 78 inline void o3(int f,int v){
 79     ins(1,1,n,w[f],w[f]+sz[f]-1,v);
 80 }
 81 inline void o4(int f){
 82     printf("%d\n",query(1,1,n,w[f],w[f]+sz[f]-1)%p);
 83 }
 84 int main(){
 85     read(n),read(m),read(r),read(p);
 86     for(register int i=1;i<=n;i++)read(num[i]);
 87     register int f,t,j,x,y,z;
 88     for(register int i=1;i<n;i++){
 89         read(f),read(t),e[f].push_back(t),e[t].push_back(f);
 90     }
 91     dep[r]=1,fa[r]=0,top[r]=r;
 92     dfs1(r); dfs2(r);
 93     for(register int i=1;i<=m;i++){
 94         read(j);
 95         if(j==1)read(x),read(y),read(z),o1(x,y,z);
 96         if(j==2)read(x),read(y),o2(x,y);
 97         if(j==3)read(x),read(z),o3(x,z);
 98         if(j==4)read(x),o4(x);
 99     }
100 }

不要点人家嘛つ﹏⊂

原文地址:https://www.cnblogs.com/Fang-Hao/p/9026978.html

时间: 2024-10-05 05:50:12

树链剖分讲解&模板的相关文章

codevs 1228 苹果树 树链剖分讲解

题目:codevs 1228 苹果树 链接:http://codevs.cn/problem/1228/ 看了这么多树链剖分的解释,几个小时后总算把树链剖分弄懂了. 树链剖分的功能:快速修改,查询树上的路径. 比如一颗树 首先,我们要把树剖分成树链.定义: fa[x]是x节点的上一层节点(就是他的爸爸). deep[x]是x节点的深度. num[x]是x节点下面的子节点的数量(包括自己) son[x]重儿子:一个节点的儿子的num[x]值最大的节点.其他的儿子都是轻儿子. 重链:重儿子连接在一起

bzoj1036 [ZJOI2008]树的统计Count 树链剖分模板题

[ZJOI2008]树的统计Count Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成 一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 Input 输入的第一行为一个整数n,表示节点的个数.接下来n – 1行,每行

BZOJ3631 松鼠的新家(树链剖分)

题目链接 松鼠的新家 差不多可以说是树链剖分的模板题了,直接维护即可. 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define REP(i,n) for(int i(0); i < (n); ++i) 6 #define rep(i,a,b) for(int i(a); i <= (b); ++i) 7 #define dec(i,a,b) for(int i(a); i >= (b); --i) 8 #

poj2763--Housewife Wind(树链剖分+线段树)

Housewife Wind Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 6898   Accepted: 1742 Description After their royal wedding, Jiajia and Wind hid away in XX Village, to enjoy their ordinary happy life. People in XX Village lived in beautif

hdu3966(树链剖分+线段树)

Aragorn's Story 题意: 给出n个营地初始士兵的数量和n-1条边,保证任意两个营地之间只有一条路径到达.现在有3种操作,如果为I,则u到v路径上的所有营地的士兵个数增加w,为D,则减少w,为Q,输出路径上所有营地的士兵总和.(总和允许为负数) 分析: 树链剖分的模板题吧,利用树链剖分求出重链,重链上的点可以通过dfs序来转换成一段子区间,利用线段树维护子区间和即可. 学习博客:大佬博客 代码: #include <map> #include <queue> #incl

HDU - 6393 Traffic Network in Numazu(树链剖分+基环树)

http://acm.hdu.edu.cn/showproblem.php?pid=6393 题意 给n个点和n条边的图,有两种操作,一种修改边权,另一种查询u到v的最短路. 分析 n个点和n条边,实际上是一棵树+一个环,如果仅仅是一棵树,那么这题就是树链剖分的模板题了. 对于环来说,可以考虑先把环中一条边提取出来,然后树链剖分,修改时用线段树,单点修改和区间求和. 查询时就考虑三种情况,u走树上到v,从u经过提取出来的边再到v(两种),取最小值. 至于取出环上一条边,用并查集搞搞. #incl

bzoj4034 树上操作 树链剖分+线段树

题目传送门 题目大意: 有一棵点数为 N 的树,以点 1 为根,且树点有权.然后有 M 个操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 思路: 由于是在刷dfs序专题的时候碰到这题,所以思路被限制了,没想树链剖分的东西,没能做出来,后来发现了一个 大佬的博客,发现也是可以做的,但是这个做法看不懂...留坑 现在用树链剖分的方法,每个点的权值就是点本身

树链剖分详解及模板

这几天学习了一下树链剖分,顺便写一下我的理解. 早上看了一下别人的讲解,云里雾里,终于算是搞懂了. 树链剖分是解决在树上进行插点问线,插线问点等一系列树上的问题 假如现在给你一棵树,然后没两条边之间有一条权值,有一些操作,1:x---y之间的最大权值是多少,2:改变x---y之间的权值 当前这样的操作有很多,如果直接用暴力的方法的话肯定不行,那么就要想一个好的方法,我们可以想一下能不能借助线段树解决,能不能想一种方法对树上的边进行编号,然后就变成区间了.那么我们就可以在线段树上进行操作了,树链剖

模板:树链剖分

“如果你会了树上dp,还会线段树……” “没错!我都会啊!” “……那你为什么写不出树链剖分?” “???” ——by勇者和路由器的对话,今天二位仍然过得十分愉快 ———————————————————————————— 因为路由器编不出来什么好题面了,所以就扔上来了一个模板题然后和勇者去玩了. (下面有树链剖分的板子代码+注释,这里只提供讲解) [ZJOI2008]树的统计Count:http://www.cnblogs.com/luyouqi233/p/7886531.html 我们发现题中