Finding Dominators in Directed Graphs (SEMI-NCA Algorithm)
概要
\(r\) を始点とする有向グラフ \(G = (V, A, r)\) において、各頂点 \(v\) を直近で支配する頂点 (an immediate dominator) を求める。
SEMI-NCA では、各頂点 \(v\) の semidominator \(sdom(v)\) を求め、この semidominator を元に徐々に dominator tree を構築する。
各頂点 \(w\) の親頂点 \(parent(w)\) から dominator tree を遡り、 \(x \le sdom(w)\) を満たす初めの頂点 \(x\) を immediate dominator とする。
dominator tree の構築での最悪計算量は \(O(|V|^2)\) になるが、実用的なケースでは高速に動作することが期待できる。
(競プロ的には、時間がかかるケースが人為的に生成される可能性がある)
計算量
\(N = |V|\), \(M = |A|\) とする
Lengauer-Tarjan Algorithm の simple version の LINK-EVAL を用いて semidominator を構築する場合は \(O(N^2 + M \log N)\)
実装
def dfs_preorder(N, G, s):
order = [-1]*N
order[s] = 0
vs = [0]*N
vs[0] = s
prt = [-1]*N
stk = [s]
it = [0]*N
cur = 0
while stk:
v = stk[-1]
while it[v] < len(G[v]):
w = G[v][it[v]]; it[v] += 1
if order[w] == -1:
prt[w] = v
stk.append(w)
order[w] = cur = cur+1
vs[cur] = w
break
else:
stk.pop()
return vs, order, prt
def dominator(N, G, s):
vs, order, prt = dfs_preorder(N, G, s)
semi = order[:]
doms = [s]*N
RG = [[] for i in range(N)]
for v in range(N):
for w in G[v]:
RG[w].append(v)
*anc, = range(N)
label = order[:]
def s_link(v, w):
anc[w] = v
def s_eval(v):
if v == anc[v]:
return label[v]
compress(v, anc[v])
return label[v]
def compress(v, w):
if w == anc[w]:
return w
anc[v] = u = compress(w, anc[w])
if label[w] < label[v]:
label[v] = label[w]
return u
for w in reversed(vs[1:]):
for v in RG[w]:
s_min = s_eval(v)
if s_min < semi[w]:
semi[w] = s_min
label[w] = semi[w]
s_link(prt[w], w)
doms[s] = s
for w in vs[1:]:
x = prt[w]
while semi[w] < order[x]:
x = doms[x]
doms[w] = x
return doms
Verified
-
AOJ: "0294: Catch a Thief": source (Python3, 1.09sec)
参考
-
Georgiadis, Loukas, Robert E. Tarjan, and Renato F. Werneck. "Finding dominators in practice." Journal of Graph Algorithms and Applications 10.1 (2006): 69-94.