weekly contest 426。
大概是今年最簡單的 Q4。
吐槽一下英文版題目,明明大部分內容和 Q4 相同,偏偏要寫不同的句子、格式,浪費一大堆時間重看。

題目

輸入兩棵無向樹,分別有 n 和 m 個節點,編號為 [0, n - 1] 和 [0, m - 1]。

輸入兩個二維整數陣列 edges1 和 edges2,長度分別為 n - 1 和 m - 1。
其中 edges1[i] = [ai, bi] 代表 ai 和 bi 之間有一條邊,而 edges2[i] = [ui, vi] 代表 ui 和 vi 之間有一條邊。

若節點 u 和 v 之間的路徑的邊數是偶數,則稱 u 是 v 的目標
注意:一個節點保證是自己的目標

回傳長度 n 個整數陣列 answer,其中 answer[i] 代表將第一棵樹中的某個節點與第二棵樹中的某個節點連邊後,第一棵樹中節點 i 的目標數量的最大值

注意:每次查詢都是獨立的。下次連邊之前,要先把上次連的邊清除掉。

解法

和 Q3 只差在目標的定義從 k 步內改成偶數距離。

同樣先不考慮連邊,只考慮第一棵樹中所有節點 i 中的目標,依然可以用 bfs (或 dfs) 求出偶數距離的節點。
但本題 n 高達 10^5,無法每個點都求一次。

設想有棵直線的樹,呈現:

1,2,3

對於節點 1 來說,有 2 個偶數距離,1 個奇數距離。
對於節點 2 來說,有 1 個偶數距離,2 個奇數距離。
對於節點 3 來說,有 2 個偶數距離,1 個奇數距離。

每一個節點的奇偶數量會和他的鄰居相反。
我們只需要對任一節點做一次 bfs,之後透過推導出所有節點的奇偶數量,也就是換根 dp


再來考慮連邊,同樣題目沒有規定要連到 i 上。

設第二棵樹從節點 0 出發,偶數距離有 even 個,奇數距離有 odd 個:

  • 把 i 連到第二棵樹的 0,會多出 odd 個偶數距離。
  • 把 i 連到第二棵樹的 0 的隔壁鄰居,會多出 even 個偶數距離。

也就是說你想要多 even 或是 odd 都行,為答案最大化,取 mx = max(even, odd)。
在第一棵做換根 dp 時,把 ans[i] 加上 mx。

時間複雜度 O(n + m)。
空間複雜度 O(n + m)。

class Solution:
    def maxTargetNodes(self, edges1: List[List[int]], edges2: List[List[int]]) -> List[int]:
        N, M = len(edges1) + 1, len(edges2) + 1
        g1 = [[] for _ in range(N)]
        g2 = [[] for _ in range(M)]
        for a, b in edges1:
            g1[a].append(b)
            g1[b].append(a)
        for a, b in edges2:
            g2[a].append(b)
            g2[b].append(a)

        # find odd/even of tree
        def bfs(g, i):
            vis = [False] * len(g)
            q = deque()
            q.append(0)
            vis[0] = True
            cnt = [1, 0] # even, odd
            parity = 0
            while q:
                parity ^= 1
                for _ in range(len(q)):
                    curr = q.popleft()
                    for adj in g[curr]:
                        if not vis[adj]:
                            vis[adj] = True
                            q.append(adj)
                            cnt[parity] += 1
            return cnt

        tree1 = bfs(g1, 0)
        tree2 = bfs(g2, 0)
        # can add odd or even of tree2
        mx = max(tree2)
        ans = [0] * N

        def dfs(i, fa, even, odd):
            ans[i] = even + mx
            for j in g1[i]:
                if j == fa:
                    continue
                dfs(j, i, odd, even)

        dfs(0, -1, tree1[0], tree1[1])

        return ans

改寫成 dfs 寫法。

class Solution:
    def maxTargetNodes(self, edges1: List[List[int]], edges2: List[List[int]]) -> List[int]:
        N, M = len(edges1) + 1, len(edges2) + 1
        g1 = [[] for _ in range(N)]
        g2 = [[] for _ in range(M)]
        for a, b in edges1:
            g1[a].append(b)
            g1[b].append(a)
        for a, b in edges2:
            g2[a].append(b)
            g2[b].append(a)

        # find odd/even of tree
        def dfs(g, i, fa, parity):
            res = [0, 0]
            res[parity] = 1
            for j in g[i]:
                if j == fa:
                    continue
                t = dfs(g, j, i, parity^1)
                res[0] += t[0]
                res[1] += t[1]
            return res

        tree1 = dfs(g1, 0, -1, 0)
        tree2 = dfs(g2, 0, -1, 0)
        # can add odd or even of tree2
        mx = max(tree2)
        ans = [0] * N

        def dfs(i, fa, even, odd):
            ans[i] = even + mx
            for j in g1[i]:
                if j == fa:
                    continue
                dfs(j, i, odd, even)

        dfs(0, -1, tree1[0], tree1[1])

        return ans