说到树链剖分,其实故事还挺多的。我记得在高中的OI经历中,我曾无数次听到这个名词,各种省赛、邀请赛貌似都会考这个东西,那时我觉得树链剖分深不可测,是我等蒟蒻不能理解的东西……然后我还记得,某年(好像是NOip2014?)有一题貌似也要用树链剖分。总之是被虐的一B,然后现在觉得这个东西也没什么难的……
好吧,言归正传。所谓树链剖分其实就是树的轻重链剖分,而树链就是指从某一父节点一直到叶子节点的一条链。下面先介绍几个概念:
1、重儿子:父节点的所有儿子中,子树节点数目最多的儿子
2、重边:连接父节点和儿子的边
3、重链:从父节点往下所有重儿子连在一起的链
有了这些定义,我们就能显而易见的发现:任何节点都只属于一条树链。然后按照这样把一棵树剖分成这样一条一条的树链,树链剖分就完成了,如图:
其中这棵树被剖分成了红、绿、蓝三条链,但是要注意,圈出来的两条边其实是不属于树链的。是的完成了这个,树链剖分就已经做完了。其实剖分很容易理解,但是问题是这样做了有什么用呢?这个坑我们后面填~接下来先说说如何实现它。
第一步当然是对于每个非叶子节点都要选取一条重边以及重儿子,这就是重链的延伸方向。如你所想,用dfs很容易实现,依次遍历该节点的子树,然后挑选可达深度最大的点作为重儿子,具体代码如下:
inline void dfs1(int u, int f, int d) //u为当前节点,f为父亲,d为当前深度{ dep[u] = d; //先记下深度 size[u] = 1; //size[]表示子树的节点数目 son[u] = 0; //son[]表示重儿子 fa[u] = f; efor(i,u) //遍历子树 { int ff = g[i].y; if (ff == f) continue; dfs1(ff, u, d + 1); size[u] += size[ff]; if (size[son[u]] < size[ff]) son[u] = ff; //选取节点数最多的点作为重儿子 }}标记完重儿子后,我们要把每条链连起来,真正形成链。那么,我们用什么表示一条链呢?我们知道,树状数组、线段树、平衡树都是能支持在一段区间里维护性质,若我们能把一条链上的节点弄在一段连续的区间中,那我们就容易处理这些问题了。于是便想到了对每个节点进行重(chong)标号,一条链上的点的新编号一定是连续的,具体的还是利用dfs来实现,代码:
inline void dfs2(int u, int tp) //tp表示当前树链的链头,其实这个操作应该叫label{ top[u] = tp; //top[]表示节点i所属树链的链头,方便查询和修改 id[u] = ++num; //id[]表示一个节点新的id(编号) Rank[id[u]]=u; //Rank[]表示新编号i对应原来的编号,即Rank[id[i]]=i if (son[u]) dfs2(son[u], tp); //先对同一条链上的重儿子标号 efor(i,u) { int ff = g[i].y; if (ff == fa[u] || ff == son[u]) continue; dfs2(ff, ff); //对其他儿子标号 }}这样通过dfs1和dfs2,我们就完成了树链剖分,现在开始填坑……
树链剖分适合解决树上的一些修改询问以及求LCA之类的问题,同过把树进行剖分,我们可以用很多数据结构维护每一条链,然后再通过求LCA,我们可以解决对于树上任意两点的询问问题。例如不断的对边权修改,求任意两点之间的最大边或最大值,这就可以用线段树对每个树链进行维护。另外,还值得一提的是树链剖分后求LCA的方法,由于我们在dfs2的时候记录了top[]数组,所以用朴素法就能很快的求出LCA,代码:
inline int LCA(int u, int v){ int tp1 = top[u], tp2 = top[v]; //tp为链头 while (tp1 != tp2) { if (dep[tp1] < dep[tp2]) //判断深度 { swap(tp1, tp2); swap(u, v); } u = fa[tp1]; //每次向上走了tp1所以速度很快 tp1 = top[u]; } return tp1;}令我不满意的是,我好像还是没有说清楚具体如何进行修改和查询,好吧,下次结合具体的题目再说把,再给自己挖一个坑……
最后说说我最不擅长的复杂度吧,由于是按照轻重链剖分的,所以有这样的定理:
1、对于轻边(u,v),size[v]<=size[u]/2
2、从根到某一点的路径上,不超过logN条轻边
3、重链总数不超过logN
因此剖分的时间复杂度为O(N);用数据结构去维护每一条链时,每次的时间复杂度为O(logN),当然你若不是用高级数据结构我也没办法咯=_=……,所以总的来说时间复杂度是O(NlogN)级别的。
最后,我又看了一下百度,我觉的百科说的还是更有道理,树链剖分是用来维护树的路径信息的一种算法?数据结构?我还是不来定义了……
对了,那个坑我会填的^_^~~~
新闻热点
疑难解答