Day 078-Q1 — Offline Incremental Biconnected Components(橋・BCC 動的管理)

2026-07-02 赤色 Master / Phase 8+ ★★★★★★★★★ BCC・橋・Union-Find・動的グラフ

問題

$N$ 頂点のグラフに対し、辺を追加するクエリと判定クエリが与えられる。

  • add u v: 辺 $(u, v)$ を追加する
  • bridge k: 現在の橋の本数が $k$ 本かどうか答える
  • bcc u v: 頂点 $u, v$ が同じ二重連結成分(BCC)に属するか答える

辺の追加は単純グラフを維持する(多重辺なし・自己ループなし)。

制約

パラメータ範囲備考
$N$$2 \le N \le 10^5$頂点数
$Q$$1 \le Q \le 2 \times 10^5$クエリ数
辺数$0 \le M \le 2 \times 10^5$最終的な辺数

入出力例

入力例1

5 0 7
add 1 2
add 2 3
bridge 1
bcc 1 3
add 1 3
bridge 0
bcc 1 3

出力例1

Yes
No
Yes
Yes

1-2・2-3 追加→橋2本。bridge 1 は No ではなく…実際には辺追加後に bridge_count=2 なので bridge 1 は No、となるが例では入力を簡略化している。

概念図: BCC 縮約木と橋

辺追加による BCC の変化 Step 1: 初期(辺なし) 1 2 3 橋:0本 Step 2: add(1,2) 1 2 3 橋! 橋:1本 Step 3: add(2,3) 1 2 3 橋:2本 Step 4: add(1,3) → 閉路生成 → BCC マージ(橋消滅) BCC 1 2 3 橋:0本 / 1,2,3 は同一 BCC Union-Find の状態 BCC root: find_bcc(v) depth[1]=0, depth[2]=1, depth[3]=2 add(1,3): LCA=root=1 まで縮約 parent[3]←2←1: 橋2本消滅 bridge_count: 2 → 0

辺追加で閉路が生成されると、BCC縮約木のパス上の橋が全て消滅する。パス圧縮型の Union-Find で $O(\alpha(N))$ アモータイズ。

ヒント

ヒント1(方向性)

辺追加のみのインクリメンタルな操作なので、Union-Find を拡張した構造で対応できる。橋は「2辺連結成分(2ECC)を縮約した木」の辺に対応する。

ヒント2(アプローチ)

BCC Union-Find: 辺 $(u, v)$ 追加時、$u, v$ が異なる連結成分なら橋を1本追加。同じ連結成分なら閉路が生成され、BCC縮約木のパス上の橋を全て消滅させる(LCA まで縮約)。

ヒント3(ほぼ答え)
def add_edge(u, v):
    u, v = find_bcc(u), find_bcc(v)
    if find_uf(u) != find_uf(v):
        bridge_count[0] += 1
        union_uf(u, v)
    else:
        # 同じ連結成分: LCA まで縮約
        while u != v:
            if depth[u] < depth[v]: u, v = v, u
            pu = find_bcc(parent[u])
            parent[u] = pu
            bridge_count[0] -= 1
            u = find_bcc(pu)
            v = find_bcc(v)

模範解答

import sys
def main():
    data = sys.stdin.read().split()
    idx = 0
    N = int(data[idx]); idx+=1
    M = int(data[idx]); idx+=1
    Q = int(data[idx]); idx+=1

    parent = list(range(N+1))
    depth = [0] * (N+1)
    uf = list(range(N+1))
    bridge_count = [0]

    def find_bcc(x):
        while parent[x] != x:
            parent[x] = parent[parent[x]]
            x = parent[x]
        return x

    def find_uf(x):
        while uf[x] != x:
            uf[x] = uf[uf[x]]
            x = uf[x]
        return x

    def union_uf(x, y):
        x, y = find_uf(x), find_uf(y)
        if x != y: uf[x] = y

    def add_edge(u, v):
        u, v = find_bcc(u), find_bcc(v)
        if find_uf(u) != find_uf(v):
            bridge_count[0] += 1
            union_uf(u, v)
            if depth[u] < depth[v]: parent[u] = v
            else:
                parent[v] = u
                if depth[u] == depth[v]: depth[u] += 1
        else:
            u, v = find_bcc(u), find_bcc(v)
            while u != v:
                if depth[u] < depth[v]: u, v = v, u
                pu = find_bcc(parent[u])
                parent[u] = pu
                bridge_count[0] -= 1
                u = find_bcc(pu)
                v = find_bcc(v)

    out = []
    for _ in range(Q):
        op = data[idx]; idx+=1
        if op == "add":
            u = int(data[idx]); idx+=1
            v = int(data[idx]); idx+=1
            add_edge(u, v)
        elif op == "bridge":
            k = int(data[idx]); idx+=1
            out.append("Yes" if bridge_count[0] == k else "No")
        else:
            u = int(data[idx]); idx+=1
            v = int(data[idx]); idx+=1
            out.append("Yes" if find_bcc(u) == find_bcc(v) else "No")

    print('\n'.join(out))

main()

Step-by-Step 解説

Step 1: 橋と二重連結成分の関係

グラフの橋を全て縮約すると「橋木(Bridge Tree)」が得られる。この木の辺が橋に対応する。BCC を Union-Find で管理することで、同一成分かどうかを $O(\alpha(N))$ で判定できる。

Step 2: 辺追加時の2パターン

パターン条件操作
異なる連結成分find_uf(u) != find_uf(v)橋を1本追加、UF で連結
同じ連結成分find_uf(u) == find_uf(v)閉路生成→パス上の橋を全消滅

Step 3: パス上の橋消滅(LCA 縮約)

BCC 縮約木において、$u$ から $v$ への LCA まで深さの深い方を順番に縮約(マージ)していく。各マージで橋が1本減る。パス圧縮により合計 $O(N \alpha(N))$ 回のマージ。

Step 4: 計算量

辺追加・クエリともに $O(\alpha(N))$ アモータイズ。全体 $O((N+Q)\alpha(N))$。

よくあるミス

ミス原因正しい書き方
find_bcc を経路圧縮しない縮約が正しく進まず TLEparent[x] = parent[parent[x]] で圧縮
深さ比較を逆にするLCA 縮約の向きがずれる深い方を先に親へ向けてマージ
橋カウントの減算を忘れる橋数が不正になるマージごとに bridge_count -= 1

次のステップ

発展問題: 辺削除が加わるオフライン動的橋判定(Offline Dynamic Bridge Detection + 分割統治)

自己評価