动态图算法(下)

如果不出意外的话这应该就是最后一篇了吧,这个坑也总算是填完了呢。

可能如果还有点其他东西没填的话好像也只有割点的 O(log^5 n) 和割边的 O(sqrt m) 了(其实还有个 Sparsi cation 用来把 Topology Tree 的 sqrt(m) 改成 sqrt(n)),也有点麻烦什么时候再说好了。

说的还是乱七八糟的,尤其是 Topology Tree 那里,但我觉得这种很麻烦的数据结构也不太容易一言两语说完吧,嘛,到我认为自己能够理解了以后再做整理吧。

原文同中篇。


在线 - MST(仅删除) O(log^2 n)

按照我们之前的方法,给每条非树边一个权值,删除每条树边的时候,在 C_x 的出边中从小到大寻找替换边即可。

时间复杂度 O(n\log^2 n)


在线 - MST O(log^4 n)

虽然我们只能处理删除,不过我们可以用二进制分组一样的方法来支持加边,我们维护全局的MST FA_0,A_1,\ldots A_{l},l=\lfloor \lg m \rfloor 这些原图的子图和它们的MST F_1,F_2,\ldots F_l ,称 A_i/F_i 中的边为局部非树边, F_i 中的边为局部树边。对于每一个 A_i 我们用一个仅支持删除的结构来维护它。

维护MST里最核心的操作是寻找替换边,为了保证能找到这条替换边,我们要求每条非树边必须在某个 A_i 里是局部非树边。这样的话删除时我们只需要枚举每个 A_i 寻找替换边,最后在所有替换边中必然可以找到全局的替换边。

我们加边的操作的形式是 update(S) ,将 S 集合的边加入到数据结构中。

我们试着描述一下插入和删除的算法:

def insert(e):
	weight, u, v = e
	if not F.connected(u, v):
		F.insert(e)
	elif F.maxWeight(u, v) > weight:
		oldEdge = F.replace(e)
		A.update([oldEdge])
	else:
		A.update([e])

def delete(e):
	replace = []
	for i in len(A):
		if e in A:
			replacementEdge = A[i].delete(e)
			if replacementEdge != ():
				replace.append(replacementEdge)
	replace = sorted(replace)
	for e in replace:
		weight, u, v = e
		if not F.connected(u, v):
			F.insert(u, v)
			break

	A.update(replace)


之后我们的主要问题就是如何实现 update 操作。

我们可以要求每个 Ai 除了 F 之外只能有 2^i 条边,然后 update 的时候从 i=0 开始扫描,如果 S\cup (A_i \backslash F_i ) > 2^i 就用 S:=S\cup A_i\backslash F_i 并继续扫描 i+1 ,否则令 k:=i 并退出。(只加入 A_i\backslash F_i 而不是 A_i 是为了避免一条边加入多次破坏复杂度,这样可以保证每条非树边都只会在一个且仅一个 A_i\backslash F_i 中出现一次(可以再看一遍插入/删除的过程验证一下))最后令 A_k:=F\cup S\cup A_k\backslash F_k 以及 \forall i<k,A_i:=\emptyset

我们对复杂度进行一下分析,由于最后 2^{k-1}<|S|\leqslant 2^k ,而所有在 S 集合中的边都从 A_i(i<k) 移动到了 A_k ,因此我们可以令 S 集合中的每条边支付 2 的势能(把原地不动的 A_k\backslash F_k 也一起支付了)来把复杂度均摊掉,每条边至多会被移动 O(\log n) 次(即势能是 \Phi(e)=l-i(e\in A_i) )。

然而非常不幸的是,插入操作可以把一条树边变成非树边从而树边的势能是 l ,而更加不幸的是删除操作可以把 O(\log n) 条非树边变成树边,从而会增加 O(\log^2 n) 的势能。

于是由于总的势能(除 F 中的边外)是 O(m\log^2 n) 的,而我们只支持删除的结构是一个均摊 O((n+m)\log^2n) 的复杂度,所以总的复杂度是 O(m\log^4 n)

然而事情并没有结束,我们不能直接让 A_k:=F\cup S\cup A_k\backslash F_k ,由于 F 以外的边只有 O(2^k) 条,我们可以找到所有边的端点集合,在 F 上求这些点的虚树,并把这棵虚树和 A_k 合并即可(虚树的边权是路径上最大边的权值,在这条最大边被删除的时候同时也要把这条虚边删除)。


至于实现起来的话,插入和删除都很容易实现,主要问题只是在求 F 的虚树上,我认为应该只用一个 Euler Tour Tree 应该就够了吧(我也不太明白为什么论文里为什么这么麻烦,我回去再去思考一下吧,另外还有一个疑点,就是论文里要求不仅每条非树边只能在一个 A_i 里出现,还要求不能存在 e\in F_j(j<i) 的情形,我还没有理解是为什么)

编辑于 2017-10-24

文章被以下专栏收录