【摘要】
树作为一类特殊的数据结构,在信息学中有着极为重要的作用,各类关于树的题目在竞赛中更是屡见不鲜。本文选取了近几年出现的关于树的路径的题目,并结合例题讲解了分治算法在此类问题上的应用。
【关键字】
树 路径 路径剖分 分治 数据结构
【序言】
树被定义为没有圈的连通图,具有以下几个性质:
由于树具有一般图所没有的特点,因此在竞赛中有着更加广泛的应用,尤其是关于树中路径的问题,即一类以路径为询问对象的题目,更是频繁的出现在各种比赛中,每一个有志于OI及ACM的选手都应该掌握这类问题的法。
分治,指的是分而治之,即将一个问题分割成一些规模较小的相互独立的子问题,以便各个击破。我们常见的是在一个线性结构上进行分治,而在本文中我们将会讲解分治算法在树结构上的运用,称之为树的分治算法。 分治往往与高效联系在一起,而树的分治正是一种用来解决树的路径问题的高效算法。 下面让我们一起来感受树的分治算法的美妙吧。
首先选取一个点将无根树转为有根树,再递归处理每一颗以根结点的儿子为根的子树。
基于边的分治:在树中选取一条边,将原树分成两棵不相交的树,递归处理。
效率分析:定理得证。
由定理1 可得,在基于点的分治中每次我们都会将树的结点个数减少一半,因此递归深度最坏是O(log N)的,在树是一条链的时候达我们知道一条路径要么过根结点,要么在一棵子树中,这启发了我们可以使用分治算法。
路径在子树中的情况只需递归处理即可,下面我们来分析如何处理路径过根结点的情况。
给定一棵含有N 个结点的带权树,其中结点分为两类,黑点和白点。
要求找到一条路径,使得经过的黑点数不超过K 个,且路径长度最大。与上题相同,我们只需要考虑过根结点的路径,其余的递归处理即可。
我们记G(i, j)表示从根的第i个儿子到其子树中某点的最优路径的长度,其中要求此路径上的黑点不超过j 个。
我们记Dep(i)表示根结点的第i个儿子到其子树内的点的路径上最多的黑点个数。
那么我们可以得到当 j >Dep(i)时,G(i, j) =G(i,Dep(i)),所以我们只需保留 j <=Dep(i)的部分,这样就可以用DFS
在O(N)的时间内算出 G。那么我们的目标就是求出 Max{G[u,L1]+G[v, L2]} 其中u!=v,且L1+ L2 = K -siBalck [Root ]。
当x 为黑点时,Black[Root] =1,否则Black[Root]= 0。
这个问题我们很容易用平衡树来做到O(N log N),从而总复杂度为O(N*logN*logN)。这里值得注意的是O(K + Nlog N)
的方法是不能够使用的,否则整个算法的时间复杂度将会变为O(NK +N*logN*logN) 。
在这里我们介绍一个不需要高级数据结构的方法,可以发现u!=v可以变为u>v.
所以关键在于如何维护Max{G[v,L2]}(v < u)。假设我们已经得到了Max{G[v,L2]}(v显然Max{G[v,L2]}(v
注意到我们并没有显式的计算出G[u],而只是保留它的前若干位,所以两个保留长度分别为Len1,Len2的合并运算的
时间复杂度为O(Max{Len1,Len2})。
我们记根结点的儿子个数为TotChild ,那么如果直接按上述方法做的话, 总的计算次数为:Dep(1)+Max{Dep(1),Dep(2)} +...+Max{Dep(1)...Dep(TotChild)},最坏将会达到O(N*N) ,幸运的是我们发现根结点的
儿子顺序对答案是不会有影响的。因此我们首先对根结点的儿子按Dep(i)为关键字排序,然后由Dep(i)从小到大的顺
序进行计算,这样我们的计算次数就变为了Dep(1)Dep(TotChild) N,所以排序之后我们的时间复杂度为
O(N)。
在这一节中我们讲述的是树的分治算法在一类路径的计算和统计问题的运用。树的分治算法之高效,我们可以从
上面的两道例题就可窥见一斑。
在这一节中我们主要讲解的是如何使用基于点的分治解决题目,而读者可以发现在基于点的分治中,子树的个数
可能会较多,这给我们的维护带来了困难。
而在上面两道例题中如果使用基于边的分治的话,算法的分析将会更加简单,这是因为基于边的分治则只需要维
护两棵子树,这样看来,基于边的分治在思考的复杂度上小于基于点的分治,这是基于边的分治的一大优势。
但是正如我们上面讲的,较大的最坏时间复杂度是基于边的分治的致命伤,但这并不说明基于边的分治无用武之
地,我们将在第三节中探讨如何使基于边的分治的时间复杂度能够得到保证。
如果使用单纯的模拟,那么对于第二种操作,虽然期望复杂度是O(log N)的,但最坏复杂度将达到O(N)。在大量
的询问下,这个算法是无法在题目要求的时限内出解的,我们需要更好的算法。
引入路径剖分考虑到虽然这颗树的边权在不断改变着,但树的形态并未改变,因此考虑将这棵树的路径进行剖分,这里介绍一
种在实践中常用的剖分方法:
轻重边路径剖分
我们将树中的边分为两类:轻边和重边。
记Size(U)表示以U 为根的子树的结点个数,令V 为U 的儿子中Size最大的一个,那么我们称边(U,V)为重边,其余边为轻边。
我们称某条路径为重路径,当且仅当它全部由重边组成。那么对于每个点到根的路径上都不超过 O(log N)
条轻边和O( lo gN)条重路径。
现在我们回到原题,对树进行轻重边路径剖分。对于询问操作,我们可以分别处理两个点到其最近公共祖先的路
径。根据性质3,路径可以分解成最多O(log N)条轻边和O(log N)条重路径,那么只需考虑如何维护这两种对象。
对于轻边,我们直接处理即可。而对于重路径,我们只需用线段树来维护。这个算法对于两种操作的时间复杂度
分别为O(log N) ,O(logN*logN)5次方可以在时限内通过本题的所有数据了。
我们首先画出一棵树及其剖分(如图(a)),我们似乎看不出有什么特殊,我们不妨把它的样子稍加改变(如图(b)),按照点到根结点路径上的轻边个数分层摆放。
原题的标准做法是利用括号序列的性质,但是括号序列是不能在有负边权的树上使用的,因此我们必须另寻他法。
注意两个结点的距离,就是路径的长度,我们试图使用路径剖分来解决此题。
一般来说路径剖分算法都是以点到根的路径作为维护对象,这道题的算法似乎与路径剖分无关,但是只要我们想到分治算法的本质是基于链的分治后,这题便可以迎刃而解。下面我们考虑如何计算路径的最高点在此条链中的最优值。对于一条链,记此链的结点个数为N,D(i),D2(i)为此
链的第i个结点向下至某个黑色结点的路径中长度的最大值和次大值。(两条路径仅在头结点处相交。如果至黑色结点
的路径不存在,那么长度记为负无穷)
我们的目标就是要求出满足与此链的重合部分在1, N的路径的 最大长度。我们可以利用线段树来维护。 对于一个区间[L,R],我们记录MaxR(P) =Max{MaxR(Rc),MaxR(Lc)+Dist(Mid,R)}
Opt(Lc),Opt(R,c),
我们记C1...Ck 表示x的k 个儿子(不包括同层结点),Li 表示Ci所在的链的线段树根结点,Cost(p)表示(x, p)的边权。那么点x向下至某个黑色结点的路径的长度集合为:
{MaxL(Li) +Cost(Ci),0} x为黑色结点对于询问操作,我们可以用一个堆存贮每条链的最优值,这样我们就可以做到每次询问的时间复杂度为O(1)。
对于修改操作,由于一个点只会影响O(log N)条链,每次都需要更改堆中的值和线段树,所以每次修改的时间复杂度为 (log ) 2 O N 。通过对路径剖分更深一步的分析,我们发现了路径剖分算法可以理解为基于链的分治,使一道看上去与路径剖分无关的题目顺利得到解决。
首先,我们试图改变选择边的标准,可惜这是改变不了算法的最坏时间复杂度的,当树的形态类似与如图所示时,无论选择哪条边,结果都是一样的。
注意到算法的复杂度分析的决定性因素是每个点的度数,我们猜想是否可以通过等价的转换,使原图转化成一个
每个点的度数是常数级别的新图呢?
我们再来回想例5,它所关注的对象是两个黑点之间的距离,这就提醒我们可以在不影响树中黑色结点之间的距离的前提下加入白色结点!
经过尝试,我们可以利用如图的方式使原图得到等价的转化。
可以看到通过巧妙的对每个结点到其儿子的路径中加入了白色结点,使之成为了类似线段树的结构,而长度为N
的线段树共有2N个结点,所以含有N 个结点的树转化后所得的新树最多包含2N 个结点。
而且这个转化给予了我们原树所没有的性质,那就是每个点的度至多为3!幸运的是,这个改变树结构的方法是可以推广的。白色结点代表的是不影响结果的中间结点,我们可以用这个方
法来解决一般的问题。
四.全文总结
我们对前几节所讲的算法作一个简单的总结。
在算法的常数方面,树的路径剖分算法是当中常数最小的算法,基于点的分治其次,基于边的分治常数较大。
在算法的应用范围方面,基于链的分治与基于点(边)的分治有所不同。前者的特点在于每次被删除结点的儿子必
将作为下一次删除的链的头结点,这一点是基于点(边)的分治无法达到的。所以前者可以用来维护路径上的点(边),但
如果维护的对象是路径的长度,后者的能力更强。
与基于点的分治比较,基于边的分治在设计高效算法的思考难度上明显小于前者。 这几个算法各有所长,需要我
们根据具体情况,灵活运用,以最佳的方式解决题目。
只要我们做到勤于思考,善于总结,我们就一定会更上一层楼!
【参考文献】