Alice and Bob have an undirected graph of n nodes and 3 types of edges:

Type 1: Can be traversed by Alice only.
Type 2: Can be traversed by Bob only.
Type 3: Can by traversed by both Alice and Bob.

Given an array edges where edges[i] = [typei, ui, vi] represents a bidirectional edge of type typei between nodes ui and vi, find the maximum number of edges you can remove so that after removing the edges, the graph can still be fully traversed by both Alice and Bob. The graph is fully traversed by Alice and Bob if starting from any node, they can reach all other nodes.
Return the maximum number of edges you can remove, or return -1 if it's impossible for the graph to be fully traversed by Alice and Bob.
 
Example 1:

Input: n = 4, edges = [[3,1,2],[3,2,3],[1,1,3],[1,2,4],[1,1,2],[2,3,4]]
Output: 2
Explanation: If we remove the 2 edges [1,1,2] and [1,1,3]. The graph will still be fully traversable by Alice and Bob. Removing any additional edge will not make it so. So the maximum number of edges we can remove is 2.

Example 2:

Input: n = 4, edges = [[3,1,2],[3,2,3],[1,1,4],[2,1,4]]
Output: 0
Explanation: Notice that removing any edge will not make the graph fully traversable by Alice and Bob.

Example 3:

Input: n = 4, edges = [[3,2,3],[1,1,2],[2,3,4]]
Output: -1
Explanation: In the current graph, Alice cannot reach node 4 from the other nodes. Likewise, Bob cannot reach 1. Therefore it's impossible to make the graph fully traversable.
 
 
Constraints:

1 <= n <= 10^5
1 <= edges.length <= min(10^5, 3 * n * (n-1) / 2)
edges[i].length == 3
1 <= edges[i][0] <= 3
1 <= edges[i][1] < edges[i][2] <= n
All tuples (typei, ui, vi) are distinct.

Difficulty: interview

Starter code:

class Solution:
    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:
        

Solutions:
["class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        if len(edges) == 0:\n            return 0 if n == 0 else -1\n        p = [i for i in range(n)]\n        def getP(ind):\n            nonlocal p\n            if p[ind] == ind:\n                return ind\n            else:\n                res = getP(p[ind])\n                p[ind] = res\n                return res\n        cnt = 0\n        for t,u,v in edges:\n            if t == 3:\n                pu,pv = getP(u-1), getP(v-1)\n                if pu != pv:\n                    p[pv] = pu\n                    cnt += 1\n        if cnt != (n - 1):\n            pa = list(p)\n            for t,u,v in edges:\n                if t == 1:\n                    pu,pv = getP(u-1), getP(v-1)\n                    if pu != pv:\n                        p[pv] = pu\n                        cnt += 1\n            targetP = getP(0)\n            for v in range(n):\n                if getP(v) != targetP:\n                    return -1\n            p = pa\n            for t,u,v in edges:\n                if t == 2:\n                    pu,pv = getP(u-1), getP(v-1)\n                    if pu != pv:\n                        p[pv] = pu\n                        cnt += 1\n            targetP = getP(0)\n            for v in range(n):\n                if getP(v) != targetP:\n                    return -1\n        return len(edges) - cnt", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        A = list(range(n))\n        \n        def fixed(i,arr):\n            if i!=arr[i]:\n                arr[i] = fixed(arr[i],arr)\n            return arr[i]\n        \n        for ty,s,t in edges:\n            if ty==3:\n                x = fixed(s-1,A)\n                y = fixed(t-1,A)\n                A[x] = y\n        \n        both_comps = sum(1 for i in range(n) if A[i] == i)\n        \n        B = A[:]\n        \n        for ty, s,t in edges:\n            if ty==1:\n                x = fixed(s-1,A)\n                y = fixed(t-1,A)\n                A[x] = y\n        \n        a_comps = sum(1 for i in range(n) if A[i] == i)\n\n        if a_comps!=1:\n            return -1\n        \n        for ty,s,t in edges:\n            if ty==2:\n                x = fixed(s-1,B)\n                y = fixed(t-1,B)\n                B[x] = y\n        \n        b_comps = sum(1 for i in range(n) if B[i] == i)\n\n        if b_comps!=1:\n            return -1\n        \n        return len(edges) - (n-a_comps-b_comps+both_comps)", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        com_conn = [i for i in range(n+1)]\n        size = [1 for i in range(n+1)]\n        com_count = [n]\n        removed = [0]\n        coed = 0\n        a_edge = list()\n        b_edge = list()\n        for i in edges:\n            if i[0] == 3:\n                self.union(i[1],i[2], com_conn, com_count, removed,size)\n                coed +=1\n            elif i[0] == 1:\n                a_edge.append(i)\n            else:\n                b_edge.append(i)\n        if com_count[0] ==1:\n            return removed[0]+len(edges)-coed\n\n        aconn = com_conn.copy()\n        asize = size.copy()\n        acom_count = com_count.copy()\n        for i in a_edge:\n            if i[0]==1:\n                self.union(i[1], i[2], aconn, acom_count, removed, asize)\n        if acom_count[0] >1:\n            return -1\n\n        bconn = com_conn.copy()\n        bsize = size.copy()\n        bcom_count = com_count.copy()\n        for i in b_edge:\n            if i[0]==2:\n                self.union(i[1], i[2], bconn, bcom_count, removed,bsize)\n        if bcom_count[0] >1:\n            return -1\n        return removed[0]\n\n\n    def find(self, p, connect):\n        while p != connect[p]:\n            p =connect[p]\n        return p\n\n    def union(self, p, q, connect, count, remove, size):\n        proot = self.find(p, connect)\n        qroot = self.find(q, connect)\n        if proot == qroot:\n            remove[0]+=1\n            return\n        if size[proot] > size[qroot]:\n            connect[qroot] = proot\n            size[proot] += size[qroot]\n        else:\n            connect[proot] = qroot\n            size[qroot] += size[proot]\n        count[0] -=1\n", "import copy\n\ndef union(subsets, u, v):\n    uroot = find(subsets, u)\n    vroot = find(subsets, v)\n    \n    if subsets[uroot][1] > subsets[vroot][1]:\n        subsets[vroot][0] = uroot\n    if subsets[vroot][1] > subsets[uroot][1]:\n        subsets[uroot][0] = vroot\n    if subsets[uroot][1] == subsets[vroot][1]:\n        subsets[vroot][0] = uroot\n        subsets[uroot][1] += 1\n    \n\ndef find(subsets, u):\n    if subsets[u][0] != u:\n        subsets[u][0] = find(subsets, subsets[u][0])\n    return subsets[u][0]\n\n\nclass Solution:\n    #kruskal's\n    #1 is alice and 2 is bob\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        subsets1 = ['1 index'] + [[x+1,0] for x in range(n)] #Alice's unionfind\n        subsets2 = ['1 index'] + [[x+1,0] for x in range(n)] #Bob's unionfind\n        \n        edges = sorted(edges, key= lambda e: -e[0])\n        e = 0 #number of total edges used\n        e1 = 0 #number of edges for Alice\n        e2 = 0 #number of edges for Bob\n        i = 0 #track position in edges list\n        \n        #start with type 3 edges\n        while e < n - 1:\n            if i == len(edges): \n                return -1\n            typ, u, v = edges[i]\n            if typ != 3: break\n            if find(subsets1, u) != find(subsets1, v):\n                union(subsets1, u, v)\n                e += 1\n            \n            i += 1\n        \n        #everything that was done to Alice applies to Bob\n        e1 = e\n        e2 = e\n        subsets2 = copy.deepcopy(subsets1)\n        \n        #once done with shared edges, do Bob's\n        while e2 < n-1:\n            if i == len(edges): \n                return -1\n            typ, u, v = edges[i]\n            if typ != 2: break\n            if find(subsets2, u) != find(subsets2, v):\n                union(subsets2, u, v)\n                e += 1\n                e2 += 1\n            i += 1\n        \n        if e2 < n - 1: \n            return -1 #if we've used all edges bob can use (types 2 and 3) and he still can't reach all nodes, ur fucked\n        \n        #now finish Alice's MST\n        while e1 < n-1:\n            if i == len(edges): \n                return -1\n            \n            typ, u, v = edges[i]\n            if find(subsets1, u) != find(subsets1, v):\n                union(subsets1, u, v)\n                e += 1\n                e1 += 1\n            i += 1\n            \n        return len(edges) - e\n            \n            \n            \n            \n        \n        \n", "class UF:\n    def __init__(self, n: int):\n        self.p, self.e = list(range(n)), 0\n        \n    def find(self, x: int):\n        if x != self.p[x]:\n            self.p[x] = self.find(self.p[x])\n        return self.p[x]\n\n    def union(self, x: int, y: int):\n        rx, ry = self.find(x), self.find(y)\n        if rx == ry: return 1\n        self.p[rx] = ry\n        self.e += 1\n        return 0\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        A, B = UF(n+1), UF(n+1)\n        ans = 0\n        for t, u, v in edges:\n            if t != 3:\n                continue\n            ans += A.union(u, v) # return 1 if connected else 0\n            B.union(u, v)\n        for t, u, v in edges:\n            if t == 3:\n                continue\n            d = A if t == 1 else B\n            ans += d.union(u, v)\n        return ans if A.e == B.e == n - 1 else -1 # merge times equal to edges\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        roots = [i for i in range(n)]\n        def find(u):\n            if u != roots[u]:\n                roots[u] = find(roots[u])\n            return roots[u]\n        def union(u,v):\n            pu,pv = find(u), find(v)\n            if pu == pv:\n                return False\n            roots[max(pu,pv)] = min(pu,pv)\n            return True\n        \n        edges = sorted(edges, reverse=True)\n        i = 0\n        connect_times = 0\n        to_remove = 0\n        while i < len(edges):\n            e = edges[i]\n            if e[0] != 3:\n                break\n            \n            res = union(e[1]-1, e[2]-1)\n            if res == True:\n                connect_times += 1\n            else:\n                to_remove += 1\n            \n            i += 1\n            \n        origin_roots = deepcopy(roots)\n        origin_connect = connect_times\n        while i < len(edges):\n            e = edges[i]\n            if e[0] != 2:\n                break\n            \n            res = union(e[1]-1, e[2]-1)\n            if res == True:\n                connect_times += 1\n            else:\n                to_remove += 1\n            \n            i += 1\n        if connect_times != n-1:\n            return -1\n        \n        connect_times = origin_connect\n        roots = origin_roots\n        while i < len(edges):\n            e = edges[i]\n            \n            res = union(e[1]-1, e[2]-1)\n            if res == True:\n                connect_times += 1\n            else:\n                to_remove += 1\n            \n            i += 1\n            \n        if connect_times != n-1:\n            return -1\n        \n        return to_remove\n        \n        \n        \n", "class DSU:\n    def __init__(self, N):\n        self.parent = [i for i in range(N)]\n        self.size = [1] * N\n        \n    def find(self, x):\n        if self.parent[x] == x:\n            return x\n        return self.find(self.parent[x])\n    \n    def union(self, x, y):\n        par_x, par_y = self.find(x), self.find(y)\n        \n        if par_x == par_y:\n            return False\n        if self.size[par_x] < self.size[par_y]:\n            par_x , par_y = par_y, par_x\n        \n        self.parent[par_y] = par_x\n        self.size[par_x] += self.size[par_y]\n        self.size[par_y] = self.size[par_x]\n    \n        return True\n    def getSize(self, x):\n        return self.size[self.find(x)]\n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        \n        alice_graph, bob_graph = DSU(n+1), DSU(n+1)\n        \n        ret = 0\n        \n        for t, start, end in edges:\n            if t == 3:\n                if not alice_graph.union(start, end):\n                    ret += 1\n                bob_graph.union(start, end)\n        \n        for t, start, end in edges:\n            if t == 1:\n                if not alice_graph.union(start, end):\n                    ret += 1\n            if t == 2:\n                if not bob_graph.union(start, end):\n                    ret += 1\n        \n        \n        return ret if alice_graph.getSize(1) == bob_graph.getSize(1) == n else -1\n                    \n            \n        \n        \n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        \n        \n        class UnionFind:\n            def __init__(self, n):\n                self.count = n  # Number of groups\n                self.root = list(range(n))  # Root node of each element\n                self.leaf = [1] * n  # Number of leaf elements\n\n            def union(self, e1, e2):\n                x, y = self.find(e1), self.find(e2)\n                if x != y:\n                    self.count -= 1\n                    if self.leaf[x] > self.leaf[y]:\n                        x, y = y, x\n                    self.leaf[y] += self.leaf[x]\n                    self.root[x] = y\n\n            def find(self, el):\n                if el == self.root[el]:\n                    return el\n                else:\n                    return self.find(self.root[el])\n\n        UFA = UnionFind(n)\n        UFB = UnionFind(n)\n\n        edges.sort(reverse=True)\n\n        res = 0\n        for t, a, b in edges:\n            a, b = a - 1, b - 1\n            if t == 3:\n                if UFA.find(a) == UFA.find(b):\n                    res += 1\n                else:\n                    UFA.union(a, b)\n                    UFB.union(a, b)\n            elif t == 2:\n                if UFB.find(a) == UFB.find(b):\n                    res += 1\n                else:\n                    UFB.union(a, b)\n            elif t == 1:\n                if UFA.find(a) == UFA.find(b):\n                    res += 1\n                else:\n                    UFA.union(a, b)\n            else:\n                pass\n\n        return res if UFA.count == 1 and UFB.count == 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges = sorted(edges, key=lambda k:k[0], reverse=True)\n        \n        A_ranks, B_ranks = [i for i in range(n+1)] ,[i for i in range(n+1)]\n        \n        def find(x, ranks):\n            if ranks[x] != x:\n                ranks[x] = find(ranks[x], ranks)\n            return ranks[x]\n        \n        def union(x, y, ranks):\n            rk_x, rk_y = find(x, ranks), find(y, ranks)\n            if rk_x < rk_y:\n                ranks[rk_y] = rk_x\n            elif rk_y < rk_x:\n                ranks[rk_x] = rk_y\n                \n        steps, e_A, e_B = 0, 0, 0\n        for i in range(len(edges)):\n            c, x, y = edges[i]\n            if c == 3:\n                A_x, A_y, B_x, B_y = find(x, A_ranks), find(y, A_ranks), find(x, B_ranks), find(y, B_ranks)\n                if A_x != A_y or B_x != B_y:\n                    union(x, y, A_ranks)\n                    union(x, y, B_ranks)\n                    e_A, e_B = e_A+1, e_B+1\n                else:\n                    steps += 1\n#        print(e_A, e_B, steps)\n        for i in range(len(edges)):\n            c, x, y = edges[i]\n            if c == 2:\n                B_x, B_y = find(x, B_ranks), find(y, B_ranks)\n                if B_x != B_y:\n                    union(x, y, B_ranks)\n                    e_B += 1\n                else:\n                    steps += 1\n#        print(e_A, e_B, steps)\n        for i in range(len(edges)):  \n            c, x, y = edges[i]\n            if c == 1:\n                A_x, A_y = find(x, A_ranks), find(y, A_ranks)\n                if A_x != A_y:\n                    union(x, y, A_ranks)\n                    e_A += 1\n                else:\n                    steps += 1\n        \n        #extra codes to actually groups elements together\n        for i in range(1, n+1):\n            find(i, A_ranks)\n            find(i, B_ranks)\n#        print(e_A, e_B, steps)\n        print(A_ranks, B_ranks)\n        \n        return steps if e_A == e_B == n-1 else -1", "import sys\ninput = sys.stdin.readline\n\nclass Unionfind:\n    def __init__(self, n):\n        self.par = [-1]*n\n        self.rank = [1]*n\n    \n    def root(self, x):\n        r = x\n        \n        while not self.par[r]<0:\n            r = self.par[r]\n        \n        t = x\n        \n        while t!=r:\n            tmp = t\n            t = self.par[t]\n            self.par[tmp] = r\n        \n        return r\n    \n    def unite(self, x, y):\n        rx = self.root(x)\n        ry = self.root(y)\n        \n        if rx==ry:\n            return\n        \n        if self.rank[rx]<=self.rank[ry]:\n            self.par[ry] += self.par[rx]\n            self.par[rx] = ry\n            \n            if self.rank[rx]==self.rank[ry]:\n                self.rank[ry] += 1\n        else:\n            self.par[rx] += self.par[ry]\n            self.par[ry] = rx\n    \n    def is_same(self, x, y):\n        return self.root(x)==self.root(y)\n    \n    def count(self, x):\n        return -self.par[self.root(x)]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1 = Unionfind(n)\n        uf2 = Unionfind(n)\n        ans = 0\n        \n        for t, u, v in edges:\n            if t==3:\n                if not uf1.is_same(u-1, v-1):\n                    uf1.unite(u-1, v-1)\n                    uf2.unite(u-1, v-1)\n                else:\n                    ans += 1\n        \n        for t, u, v in edges:\n            if t==1:\n                if not uf1.is_same(u-1, v-1):\n                    uf1.unite(u-1, v-1)\n                else:\n                    ans += 1\n            elif t==2:\n                if not uf2.is_same(u-1, v-1):\n                    uf2.unite(u-1, v-1)\n                else:\n                    ans += 1\n        \n        if len(set(uf1.root(i) for i in range(n)))>1:\n            return -1\n        \n        if len(set(uf2.root(i) for i in range(n)))>1:\n            return -1\n        \n        return ans", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        self.father_alice = [i for i in range(n + 1)]\n        self.father_bob = [i for i in range(n + 1)]\n        \n        res = 0\n        for type, u, v in edges:\n            if type == 3:\n                res += self.connect(u, v, True)\n                self.connect(u, v, False)\n        \n        for type, u, v in edges:\n            if type == 1:\n                res += self.connect(u, v, True)\n            elif type == 2:\n                res += self.connect(u, v, False)\n        \n        \n        if self.check_valid(True) and self.check_valid(False):\n            return res\n        return -1\n    \n    \n    def find(self, x, is_alice):\n        if is_alice:\n            if self.father_alice[x] == x:\n                return self.father_alice[x]\n            self.father_alice[x] = self.find(self.father_alice[x], True)\n            return self.father_alice[x]\n        \n        else:\n            if self.father_bob[x] == x:\n                return self.father_bob[x]\n            self.father_bob[x] = self.find(self.father_bob[x], False)\n            return self.father_bob[x]\n        \n    def connect(self, a, b, is_alice):\n        if is_alice:\n            root_a = self.find(a, True)\n            root_b = self.find(b, True)\n            if root_a != root_b:\n                self.father_alice[max(root_a, root_b)] = min(root_a, root_b)\n                return 0\n            return 1\n        \n        else:\n            root_a = self.find(a, False)\n            root_b = self.find(b, False)\n            if root_a != root_b:\n                self.father_bob[max(root_a, root_b)] = min(root_a, root_b)\n                return 0\n            return 1\n        \n    def check_valid(self, is_alice):\n        if is_alice:\n            root = self.find(1, True)\n            for i in range(1, len(self.father_alice)):\n                if self.find(i, True) != root:\n                    return False\n            return True\n        \n        else:\n            root = self.find(1, False)\n            for i in range(1, len(self.father_bob)):\n                if self.find(i, False) != root:\n                    return False\n            return True", "class DSU:\n    def __init__(self, n: int):\n        self.p = list(range(n))\n        self.e = 0\n    \n    def find(self, x: int) -> int:\n        if x != self.p[x]: self.p[x] = self.find(self.p[x])\n        return self.p[x]\n    \n    def merge(self, x: int, y: int) -> int:\n        rx, ry = self.find(x), self.find(y)\n        if rx == ry: return 1\n        self.p[rx] = ry\n        self.e += 1\n        return 0\n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        A, B = DSU(n + 1), DSU(n + 1)    \n        ans = 0\n        for t, x, y in edges:\n            if t != 3: continue\n            ans += A.merge(x, y)\n            B.merge(x, y)\n        for t, x, y in edges:\n            if t == 3: continue\n            d = A if t == 1 else B\n            ans += d.merge(x, y)\n        return ans if A.e == B.e == n - 1 else -1", "import copy\n\ndef union(subsets, u, v):\n    uroot = find(subsets, u)\n    vroot = find(subsets, v)\n    \n    if subsets[uroot][1] > subsets[vroot][1]:\n        subsets[vroot][0] = uroot\n    if subsets[vroot][1] > subsets[uroot][1]:\n        subsets[uroot][0] = vroot\n    if subsets[uroot][1] == subsets[vroot][1]:\n        subsets[vroot][0] = uroot\n        subsets[uroot][1] += 1\n    \n\ndef find(subsets, u):\n    if subsets[u][0] != u:\n        subsets[u][0] = find(subsets, subsets[u][0])\n    return subsets[u][0]\n\n\nclass Solution:\n    #kruskal's\n    #1 is alice and 2 is bob\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        mst1 = set() #set for Alice's MST\n        mst2 = set() #set for Bob's MST\n        subsets1 = ['1 index'] + [[x+1,0] for x in range(n)] #Alice's unionfind\n        subsets2 = ['1 index'] + [[x+1,0] for x in range(n)] #Bob's unionfind\n        \n        edges = sorted(edges, key= lambda e: -e[0])\n        e = 0 #number of total edges used\n        e1 = 0 #number of edges for Alice\n        e2 = 0 #number of edges for Bob\n        i = 0 #track position in edges list\n        \n        #start with type 3 edges\n        while e < n - 1:\n            if i == len(edges): \n                return -1\n            typ, u, v = edges[i]\n            if typ != 3: break\n            if find(subsets1, u) != find(subsets1, v):\n                union(subsets1, u, v)\n                mst1.add(u)\n                mst1.add(v)\n                e += 1\n            \n            i += 1\n        \n        #everything that was done to Alice applies to Bob\n        e1 = e\n        e2 = e\n        mst2 = mst1.copy()\n        subsets2 = copy.deepcopy(subsets1)\n        \n        #once done with shared edges, do Bob's\n        while e2 < n-1:\n            if i == len(edges): \n                return -1\n            typ, u, v = edges[i]\n            if typ != 2: break\n            if find(subsets2, u) != find(subsets2, v):\n                union(subsets2, u, v)\n                mst2.add(u)\n                mst2.add(v)\n                e += 1\n                e2 += 1\n            i += 1\n        \n        if len(mst2) < n: \n            return -1 #if we've used all edges bob can use (types 2 and 3) and he still can't reach all nodes, ur fucked\n        \n        #now finish Alice's MST\n        while e1 < n-1:\n            if i == len(edges): \n                return -1\n            \n            typ, u, v = edges[i]\n            if find(subsets1, u) != find(subsets1, v):\n                union(subsets1, u, v)\n                mst1.add(u)\n                mst1.add(v)\n                e += 1\n                e1 += 1\n            i += 1\n            \n        return len(edges) - e\n            \n            \n            \n            \n        \n        \n", "class DSU():\n    def __init__(self, n):\n        self.parent = [i for i in range(n)]\n        self.rank = [0 for _ in range(n)]\n        self.size = n\n    def find(self, x):\n        if x != self.parent[x]:\n            self.parent[x] = self.find(self.parent[x])\n        return self.parent[x]\n    def union(self, x, y):\n        xp, yp = self.find(x), self.find(y)\n        if xp == yp:\n            return False\n        if self.rank[xp] < self.rank[yp]:\n            self.parent[xp] = yp\n        elif self.rank[xp] > self.rank[yp]:\n            self.parent[yp] = xp\n        else:\n            self.parent[xp] = yp\n            self.rank[yp] += 1\n        self.size -= 1\n        return True\n    def getSize(self):\n        return self.size\n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        dsu = DSU(n + 1)\n        typeEdges = collections.defaultdict(list)\n        for t, i, j in edges:\n            typeEdges[t].append((i, j))\n        res = 0\n        for i, j in typeEdges[3]:\n            if not dsu.union(i, j):\n                res += 1\n        if dsu.getSize() == 2:\n            return res + sum(len(v) for k, v in typeEdges.items() if k in [1, 2])\n        for i, j in typeEdges[1]:\n            if not dsu.union(i, j):\n                res += 1\n        if dsu.getSize() > 2:\n            return -1\n        dsu1 = DSU(n + 1)\n        for i, j in typeEdges[3]:\n            dsu1.union(i, j)\n        for i, j in typeEdges[2]:\n            if not dsu1.union(i, j):\n                res += 1\n        if dsu1.getSize() > 2:\n            return -1\n        return res", "class UFset:\n    \n    def __init__(self, n):\n        self.parents = list(range(n))\n        self.ranks = [1] * n\n        self.size = 1\n        \n    def find(self, x):\n        if x != self.parents[x]:\n            self.parents[x] = self.find(self.parents[x])\n        return self.parents[x]\n\n    def union(self, x, y):\n        px, py = self.find(x), self.find(y)\n        if px == py:\n            return False\n        if self.ranks[x] > self.ranks[y]:\n            px, py = py, px\n        self.parents[px] = py\n        self.ranks[py] += 1\n        self.size += 1\n        return True\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        alice = UFset(n)\n        bob = UFset(n)\n        res = 0\n        for type_i, v, w in edges:\n            v -= 1; w -= 1\n            if type_i == 3:\n                a = alice.union(v, w)\n                b = bob.union(v, w)\n                if not a and not b:\n                    res += 1\n        for type_i, v, w in edges:\n            v -= 1; w -= 1\n            if type_i == 1:\n                if not alice.union(v, w):\n                    res += 1\n        for type_i, v, w in edges:\n            v -= 1; w -= 1\n            if type_i == 2:\n                if not bob.union(v, w):\n                    res += 1\n        return res if alice.size == bob.size == n else -1", "class DSU:\n  def __init__(self, n):\n    self.p = [-1]*(n+1)\n    self.r = [0]*(n+1)\n    \n  def find_parent(self, x):\n    if self.p[x]==-1:\n      return x\n    self.p[x] = self.find_parent(self.p[x]) # path compression\n    return self.p[x]\n  \n  def union(self, a, b):\n    pa = self.find_parent(a)\n    pb = self.find_parent(b)\n    if pa==pb: return False\n    if self.r[pa]<=self.r[pb]:\n      self.p[pb] = pa     # here rank can be adding\n      self.r[pa] += 1\n    else:\n      self.p[pa] = pb\n      self.r[pb] += 1\n      \n    return True\n  \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n      edges = sorted(edges, key=lambda x: -x[0])\n      dsu_alice = DSU(n)\n      dsu_bob = DSU(n)\n      res = 0\n      \n      for e in edges:\n        if e[0]==3:\n          au = dsu_alice.union(e[1],e[2])\n          bu = dsu_bob.union(e[1],e[2])\n          if not au and not bu:\n            res += 1\n        elif e[0]==1:\n          if not dsu_alice.union(e[1],e[2]):\n            res += 1\n        else:\n          if not dsu_bob.union(e[1],e[2]):\n            res += 1\n        # print (e, res) \n      \n      ap = 0\n      bp = 0\n      for i in range(1, n+1):\n        if ap and dsu_alice.find_parent(i)!=ap:\n          return -1\n        else: ap = dsu_alice.find_parent(i)\n        if bp and dsu_bob.find_parent(i)!=bp:\n          return -1\n        else: bp = dsu_bob.find_parent(i)\n      return res\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        \n        \n        \n        class UnionFind:\n            def __init__(self, n):\n                self.group = n              # Number of groups\n                self.rank = [1] * n         # Avoid infinite recursion\n                self.root = list(range(n))  # Root node of each element\n\n            def union(self, e1, e2):\n                x = self.find(e1)\n                y = self.find(e2)\n                if x != y:\n                    self.group -= 1\n                    if self.rank[x] > self.rank[y]:\n                        x, y = y, x\n                    self.rank[y] += self.rank[x]\n                    self.root[x] = y\n\n            def find(self, el):\n                if el == self.root[el]:\n                    return el\n                else:\n                    return self.find(self.root[el])\n\n        UFA = UnionFind(n)\n        UFB = UnionFind(n)\n\n        edges.sort(reverse=True)\n\n        res = 0\n        for t, a, b in edges:\n            a, b = a - 1, b - 1\n            if t == 3:\n                if UFA.find(a) == UFA.find(b):\n                    res += 1\n                else:\n                    UFA.union(a, b)\n                    UFB.union(a, b)\n            elif t == 2:\n                if UFB.find(a) == UFB.find(b):\n                    res += 1\n                else:\n                    UFB.union(a, b)\n            elif t == 1:\n                if UFA.find(a) == UFA.find(b):\n                    res += 1\n                else:\n                    UFA.union(a, b)\n            else:\n                pass\n\n        return res if UFA.group == 1 and UFB.group == 1 else -1", "import copy\n\ndef union(subsets, u, v):\n    uroot = find(subsets, u)\n    vroot = find(subsets, v)\n    \n    if subsets[uroot][1] > subsets[vroot][1]:\n        subsets[vroot][0] = uroot\n    if subsets[vroot][1] > subsets[uroot][1]:\n        subsets[uroot][0] = vroot\n    if subsets[uroot][1] == subsets[vroot][1]:\n        subsets[vroot][0] = uroot\n        subsets[uroot][1] += 1\n    \n\ndef find(subsets, u):\n    if subsets[u][0] != u:\n        subsets[u][0] = find(subsets, subsets[u][0])\n    return subsets[u][0]\n\n\nclass Solution:\n    #kruskal's\n    #1 is alice and 2 is bob\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        mst1 = set() #set for Alice's MST\n        mst2 = set() #set for Bob's MST\n        subsets1 = ['1 index'] + [[x+1,0] for x in range(n)] #Alice's unionfind\n        subsets2 = ['1 index'] + [[x+1,0] for x in range(n)] #Bob's unionfind\n        \n        edges = sorted(edges, key= lambda e: -e[0])\n        e = 0 #number of total edges used\n        e1 = 0 #number of edges for Alice\n        e2 = 0 #number of edges for Bob\n        i = 0 #track position in edges list\n        \n        #start with type 3 edges\n        while e < n - 1:\n            if i == len(edges): \n                return -1\n            typ, u, v = edges[i]\n            if typ != 3: break\n            if find(subsets1, u) != find(subsets1, v):\n                union(subsets1, u, v)\n                mst1.add(u)\n                mst1.add(v)\n                e += 1\n            \n            i += 1\n        \n        #everything that was done to Alice applies to Bob\n        e1 = e\n        e2 = e\n        mst2 = mst1.copy()\n        subsets2 = copy.deepcopy(subsets1)\n        \n        #once done with shared edges, do Bob's\n        while e2 < n-1:\n            if i == len(edges): \n                return -1\n            typ, u, v = edges[i]\n            if typ != 2: break\n            if find(subsets2, u) != find(subsets2, v):\n                union(subsets2, u, v)\n                mst2.add(u)\n                mst2.add(v)\n                e += 1\n                e2 += 1\n            i += 1\n        \n        if len(mst2) < n: \n            return -1 #if we've used all edges bob can use (types 2 and 3) and he still can't reach all nodes, ur fucked\n        \n        #now finish Alice's MST\n        while e1 < n-1:\n            if i == len(edges): \n                return -1\n            \n            typ, u, v = edges[i]\n            if find(subsets1, u) != find(subsets1, v):\n                union(subsets1, u, v)\n                \n                e += 1\n                e1 += 1\n            i += 1\n            \n        return len(edges) - e\n            \n            \n            \n            \n        \n        \n", "import copy\n\nclass DJ_DS():\n    def __init__(self, n):\n        self.n = n\n        self.parent = [i for i in range(n)]\n        self.rank = [0 for i in range(n)]\n        self.nb_edges = 0\n    \n    def find_parent(self,i): # faster with path compression\n        if self.parent[i] != i:\n            self.parent[i] = self.find_parent(self.parent[i])\n        return self.parent[i]\n        \n    def union(self,i,j):\n        p_i = self.find_parent(i)\n        p_j = self.find_parent(j)\n        \n        if p_i != p_j:\n            self.nb_edges += 1\n            if self.rank[p_i] < self.rank[p_j]:\n                self.parent[p_i] = p_j\n            else:\n                self.parent[p_j] = p_i\n                if self.rank[p_i] == self.rank[p_j]:\n                    self.rank[p_i] += 1\n                \n    def perform_merge(self, edges):\n        for [u,v] in edges:\n            self.union(u,v)\n            \n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        nb_edges = len(edges)\n        # list of edges of each color individually\n        type1, type2, type3 = [], [], []\n        for [t,u,v] in edges:\n            if t == 1:\n                type1.append([u-1,v-1])\n            elif t == 2:\n                type2.append([u-1,v-1])\n            else:\n                type3.append([u-1,v-1])\n        \n        # Count nb_edges with type 3 only in max forest\n        dj_3 = DJ_DS(n)\n        dj_3.perform_merge(type3)\n        sol_3 = dj_3.nb_edges\n        dj_1 = copy.deepcopy(dj_3)\n        dj_2 = copy.deepcopy(dj_3)\n        \n        # From type 3 forest add edges from type 1 to see if spanning tree, if not return -1\n        dj_1.perform_merge(type1)\n        if dj_1.nb_edges < n-1:\n            return -1\n        \n        # From type 3 forest add edges from type 2 to see if spanning tree, if not return -1\n        dj_2.perform_merge(type2)\n        if dj_2.nb_edges < n-1:\n            return -1\n        \n        return (nb_edges - (sol_3 + 2 * (n-1 - sol_3)))", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges.sort(key=lambda x: -x[0])\n        uf1 = UnionFind(n+1)\n        uf2 = UnionFind(n+1)\n        \n        e1 = e2 = 0\n        res = 0\n        \n        for type_, node1, node2 in edges:\n            \n            if type_ == 3:\n                val1 = uf1.union(node1, node2) \n                val2 = uf2.union(node1, node2)\n                res += val1 or val2\n                e1 += val1\n                e2 += val2\n            \n            if type_ == 1:\n                val = uf1.union(node1, node2)\n                res += val\n                e1 += val\n            \n            else:\n                val = uf2.union(node1, node2)\n                res += val\n                e2 += val\n        \n        if e1 == e2 == n-1:\n            # print(res)\n            return len(edges) - res\n        return -1\n    \nclass UnionFind:\n    def __init__(self, n):\n        self.parent = list(range(n))\n    \n    def union(self, x, y):\n        rootx = self.find(x)\n        rooty = self.find(y)\n        \n        if rootx == rooty:\n            return False\n        \n        self.parent[rooty] = rootx\n        return True\n    \n    def find(self, x):\n        while x != self.parent[x]:\n            self.parent[x] = self.parent[self.parent[x]]\n            x = self.parent[x]\n        return x\n        \n        \n", "class UnionFind:\n    def __init__(self, n):\n        self.count = n\n        self.parent = list(range(n))\n        self.rank = [1]*n\n        \n    def find(self, p):\n        if p != self.parent[p]: \n            self.parent[p] = self.find(self.parent[p])\n        return self.parent[p]\n    \n    def union(self, p, q):\n        prt, qrt = self.find(p), self.find(q)\n        if prt == qrt: return False\n        self.count -= 1\n        if self.rank[prt] > self.rank[qrt]: prt, qrt = qrt, prt\n        self.parent[prt] = qrt\n        self.rank[qrt] += self.rank[prt]\n        return True\n    \n        \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        ufa = UnionFind(n) # for Alice\n        ufb = UnionFind(n) # for Bob\n        \n        ans = 0\n        edges.sort(reverse=True) \n        for t, u, v in edges: \n            u, v = u-1, v-1\n            if t == 3: # Alice & Bob\n                if not (ufa.union(u, v) and ufb.union(u, v)): ans += 1\n            elif t == 2: # Bob only\n                if not ufb.union(u, v): ans += 1\n            else: # Alice only\n                if not ufa.union(u, v): ans += 1\n        return ans if ufa.count == 1 and ufb.count == 1 else -1", "class DSU:\n    def __init__(self, n):\n        self.p=list(range(n))\n        self.e=0\n    def find(self,x):\n        if x!=self.p[x]:\n            self.p[x]=self.find(self.p[x])\n        return self.p[x]\n    \n    def merge(self,x, y):\n        rx,ry=self.find(x), self.find(y)\n        if rx==ry: return 1\n        self.p[rx]=ry\n        self.e+=1   # merged vertice\n        return 0\n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        A,B=DSU(n+1), DSU(n+1)\n        ans=0\n        for t,x,y in edges:\n            if t!=3: continue\n            ans+=A.merge(x,y)\n            B.merge(x,y)\n        for t, x, y in edges:\n            if t==3: continue\n            d=A if t==1 else B\n            ans+=d.merge(x,y)\n        return ans if A.e==B.e==n-1 else -1\n", "class UFset:\n    \n    def __init__(self, n):\n        self.parents = list(range(n))\n        self.ranks = [1] * n\n        self.size = 1\n        \n    def find(self, x):\n        if x != self.parents[x]:\n            self.parents[x] = self.find(self.parents[x])\n        return self.parents[x]\n\n    def union(self, x, y):\n        px, py = self.find(x), self.find(y)\n        if px == py:\n            return False\n        if self.ranks[x] < self.ranks[y]:\n            px, py = py, px\n        self.parents[px] = py\n        self.ranks[py] += 1\n        self.size += 1\n        return True\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        alice = UFset(n)\n        bob = UFset(n)\n        res = 0\n        for type_i, v, w in edges:\n            v -= 1; w -= 1\n            if type_i == 3:\n                a = alice.union(v, w)\n                b = bob.union(v, w)\n                if not a and not b:\n                    res += 1\n        for type_i, v, w in edges:\n            v -= 1; w -= 1\n            if type_i == 1:\n                if not alice.union(v, w):\n                    res += 1\n            elif type_i == 2:\n                if not bob.union(v, w):\n                    res += 1\n            \n        return res if alice.size == bob.size == n else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        \n        root = [-1] * (n + 1)\n        \n        ans = 0\n        \n        def getroot(u):\n            \n            while root[u] >= 0:\n                u = root[u]                \n            return u\n        \n        def union(u, v):\n            t = root[u] + root[v]\n            \n            if root[u] < root[v]:\n                root[v] = u\n                root[u] = t\n            else:\n                root[v] = t\n                root[u] = v\n                \n        \n        for t, u, v in edges:\n            if t != 3:\n                continue\n                \n            rootu = getroot(u)\n            rootv = getroot(v)\n            \n            if rootu == rootv:\n                ans += 1\n            else:\n                union(rootu, rootv)\n                \n        temp_root = list(root)\n        \n        for t, u, v in edges:\n            if t != 1:\n                continue\n                \n            rootu = getroot(u)\n            rootv = getroot(v)\n            \n            if rootu == rootv:\n                ans += 1\n            else:\n                union(rootu, rootv)\n                \n        if root.count(-1) > 1:\n            return -1\n                \n        root = list(temp_root)\n        \n        for t, u, v in edges:\n            if t != 2:\n                continue\n                \n            rootu = getroot(u)\n            rootv = getroot(v)\n            \n            if rootu == rootv:\n                ans += 1\n            else:\n                union(rootu, rootv)      \n                \n        if root.count(-1) > 1:\n            return -1\n                \n                \n        return ans\n        \n            \n", "class Solution:\n    '''\n    Intuition\n    Add Type3 first, then check Type 1 and Type 2.\n\n\n    Explanation\n    Go through all edges of type 3 (Alice and Bob)\n    If not necessary to add, increment res.\n    Otherwith increment e1 and e2.\n\n    Go through all edges of type 1 (Alice)\n    If not necessary to add, increment res.\n    Otherwith increment e1.\n\n    Go through all edges of type 2 (Bob)\n    If not necessary to add, increment res.\n    Otherwith increment e2.\n\n    If Alice's'graph is connected, e1 == n - 1 should valid.\n    If Bob's graph is connected, e2 == n - 1 should valid.\n    In this case we return res,\n    otherwise return -1.\n\n\n    Complexity\n    Time O(E), if union find with compression and rank\n    Space O(E)\n\n    '''\n    def maxNumEdgesToRemove(self, n, edges):\n        # Union find\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n\n        res = e1 = e2 = 0\n\n        # Alice and Bob\n        root = list(range(n + 1))\n        for t, i, j in edges:\n            if t == 3:\n                if uni(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        root0 = root[:]\n\n        # only Alice\n        for t, i, j in edges:\n            if t == 1:\n                if uni(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n\n        # only Bob\n        root = root0\n        for t, i, j in edges:\n            if t == 2:\n                if uni(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if e1 == e2 == n - 1 else -1\n", "class UnionFind:\n    def __init__(self, n):\n        self.root = [i for i in range(n + 1)]\n        self.forests = n\n        \n    def unite(self, a, b):\n        self.forests -= 1\n        self.root[self.find(a)] = self.find(b)\n        \n    def find(self, a):\n        if self.root[a] != a:\n            self.root[a] = self.find(self.root[a])\n        return self.root[a]\n        \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        ufA, ufB = UnionFind(n), UnionFind(n)\n        edges.sort(key = lambda x : -x[0])\n        res = 0\n        for t, a, b in edges:\n            if 3 == t:\n                if ufA.find(a) == ufA.find(b) and ufB.find(a) == ufB.find(b):\n                    res += 1\n                if ufA.find(a) != ufA.find(b):\n                    ufA.unite(a, b)\n                if ufB.find(a) != ufB.find(b):\n                    ufB.unite(a, b)\n            elif t == 1:\n                if ufA.find(a) == ufA.find(b):\n                    # should remove this edge\n                    res += 1\n                else:\n                    ufA.unite(a, b)\n            else:\n                if ufB.find(a) == ufB.find(b):\n                    # should remove this edge\n                    res += 1\n                else:\n                    ufB.unite(a, b)\n        if ufA.forests > 1 or ufB.forests > 1:\n            return -1\n        return res", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        parenta = [x for x in range(n+1)]\n        parentb = [x for x in range(n+1)]\n        \n        def ufind(parent, x):\n            if parent[x] != x:\n                parent[x] = ufind(parent, parent[x])\n            return parent[x]\n        \n        def uunion(parent, a, b):\n            aa = ufind(parent, a)\n            bb = ufind(parent, b)\n            parent[aa] = bb\n            \n        edges.sort(key = lambda x: (-x[0]))\n        \n        count = 0\n        for t, u, v in edges:\n            if t == 3:\n                if ufind(parenta, u) != ufind(parenta, v) or ufind(parentb, u) != ufind(parentb, v):\n                    uunion(parenta, u, v)\n                    uunion(parentb, u, v)\n                else:\n                    count += 1\n            elif t == 2:\n                if ufind(parentb, u) != ufind(parentb, v):\n                    uunion(parentb, u, v)\n                else:\n                    count += 1\n            elif t == 1:\n                if ufind(parenta, u) != ufind(parenta, v):\n                    uunion(parenta, u, v)\n                else:\n                    count += 1\n        \n        roota = ufind(parenta, 1)\n        rootb = ufind(parentb, 1)\n        for x in range(1, n+1):\n            if ufind(parenta, x) != roota or ufind(parentb, x) != rootb:\n                return -1\n        \n        return count\n\n#         parenta = [x for x in range(n+1)]\n#         parentb = [x for x in range(n+1)]\n        \n#         def ufind(parent, x):\n#             if parent[x] != x:\n#                 parent[x] = ufind(parent, parent[x])\n#             return parent[x]\n        \n#         def uunion(parent, a, b):\n#             ua = ufind(parent, a)\n#             ub = ufind(parent, b)\n            \n#             parent[ua] = ub\n            \n#         edges.sort(key=lambda x: (-x[0]))\n        \n#         count = 0\n#         for t, u, v in edges:\n#             if t == 3:\n#                 if ufind(parenta, u) != ufind(parenta, v) or ufind(parentb, u) != ufind(parentb, v):\n#                     uunion(parenta, u, v)\n#                     uunion(parentb, u, v)\n#                 else:\n#                     count += 1\n#             elif t == 2:\n#                 if ufind(parentb, u) != ufind(parentb, v):\n#                     uunion(parentb, u, v)\n#                 else:\n#                     count += 1\n#             else:\n#                 if ufind(parenta, u) != ufind(parenta, v):\n#                     uunion(parenta, u, v)\n#                 else:\n#                     count += 1\n            \n#         roota = ufind(parenta, 1)\n#         rootb = ufind(parentb, 1)\n#         for x in range(1, n+1):\n#             if ufind(parenta, x) != roota or ufind(parentb, x) != rootb:\n#                 return -1\n            \n#         return count\n", "\nfrom collections import defaultdict\nimport copy\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        def find(s, i):\n            if s[i] != i:\n                s[i] = find(s, s[i])\n            return s[i]\n        \n        def union(s, i, j):\n            if i > j:\n                i, j = j, i\n            s[find(s, j)] = s[find(s, i)]\n        \n        def is_connected(s, i, j):\n            return find(s, i) == find(s, j)\n        \n        def is_full_connect(s):\n            return all(is_connected(s, i, i+1) for i in range(len(s) - 1))\n\n        \n        d = defaultdict(set)\n        res = 0\n        uf = list([i for i in range(n)])\n        for t, i, j in edges:\n            d[t].add((i-1, j-1))\n        for i, j in d[3]:\n            if is_connected(uf, i, j):\n                res += 1\n            else:\n                union(uf, i, j)\n        uf1, uf2 = copy.copy(uf), copy.copy(uf)\n\n        for i, j in d[1]:\n            if is_connected(uf1, i, j):\n                res += 1\n            else:\n                union(uf1, i, j)\n\n        for i, j in d[2]:\n            if is_connected(uf2, i, j):\n                res += 1\n            else:\n                union(uf2, i, j)\n\n        if not is_full_connect(uf1) or not is_full_connect(uf2):\n            return -1\n\n        return res\n", "class UnionFind:\n    def __init__(self, n):\n        self.parent = list(range(n))\n        self.rank = [1] * n\n\n    def find(self, i):\n        if self.parent[i] != i:\n            self.parent[i] = self.find(self.parent[i])\n        return self.parent[i]\n\n    def union(self, x, y):\n        px = self.find(x)\n        py = self.find(y)\n        if px == py:\n            return px\n        if self.rank[px] > self.rank[py]:\n            px, py = py, px\n        self.parent[px] = py\n        if self.rank[px] == self.rank[py]:\n            self.rank[py] += 1\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        both = [(a - 1, b - 1) for (t, a, b) in edges if t == 3]\n        alice = [(a - 1, b - 1) for (t, a, b) in edges if t == 1]\n        bob = [(a - 1, b - 1) for (t, a, b) in edges if t == 2]\n\n        uf_alice = UnionFind(n)\n        uf_bob = UnionFind(n)\n        count = 0\n        for a, b in both:\n            if uf_alice.find(a) != uf_alice.find(b):\n                uf_alice.union(a, b)\n                uf_bob.union(a, b)\n                count += 1\n\n        for a, b in alice:\n            if uf_alice.find(a) != uf_alice.find(b):\n                uf_alice.union(a, b)\n                count += 1\n\n        for a, b in bob:\n            if uf_bob.find(a) != uf_bob.find(b):\n                uf_bob.union(a, b)\n                count += 1\n\n        p_alice = set([uf_alice.find(i) for i in range(n)])\n        if len(p_alice) > 1:\n            return -1\n\n        p_bob = set([uf_bob.find(i) for i in range(n)])\n        if len(p_bob) > 1:\n            return -1\n\n        return len(edges) - count\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        type1 = [[i-1, j-1] for t, i, j in edges if t == 1]\n        type2 = [[i-1, j-1] for t, i, j in edges if t == 2]\n        type3 = [[i-1, j-1] for t, i, j in edges if t == 3]\n\n        def helper(type1, other, fa, c=False):\n            def getfa(i):\n                if fa[i] != i:\n                    fa[i] = getfa(fa[i])\n                return fa[i]\n            connect_count = 0 \n            ret = len(other)\n            for i, j in type1:\n                if getfa(i) != getfa(j):\n                    fa[getfa(i)] = getfa(j)\n                    connect_count += 1\n            for i, j in other:\n                if getfa(i) != getfa(j):\n                    fa[getfa(i)] = getfa(j)\n                    connect_count += 1\n                    ret -= 1\n            # print(fa, connect_count, ret)\n            if c == True or connect_count == len(fa) - 1:\n                return ret\n            return -1\n        \n        \n        t1_count = helper(type3, type1, list(range(n)))\n        if t1_count < 0:\n            return -1\n        t2_count = helper(type3, type2, list(range(n)))\n        if t2_count < 0:\n            return -1\n        t3_count = helper([], type3, {i:i for i in set([x for x, y in type3] + [y for x, y in type3])}, True)\n        return t1_count + t2_count + t3_count\n            \n        \n", "class Solution:\n    '''\n    Intuition\n    Add Type3 first, then check Type 1 and Type 2.\n\n    Explanation\n    Go through all edges of type 3 (Alice and Bob)\n    If not necessary to add, increment res.\n    Otherwith increment e1 and e2.\n\n    Go through all edges of type 1 (Alice)\n    If not necessary to add, increment res.\n    Otherwith increment e1.\n\n    Go through all edges of type 2 (Bob)\n    If not necessary to add, increment res.\n    Otherwith increment e2.\n\n    If Alice's'graph is connected, e1 == n - 1 should valid.\n    If Bob's graph is connected, e2 == n - 1 should valid.\n    In this case we return res,\n    otherwise return -1.\n\n    Complexity\n    Time O(E), if union find with compression and rank\n    Space O(E)\n\n    '''\n    def maxNumEdgesToRemove(self, n, edges):\n        # Union find\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n\n        res = e1 = e2 = 0\n\n        # Alice and Bob\n        root = list(range(n + 1))\n        for t, i, j in edges:\n            if t == 3:\n                if uni(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        root0 = root[:]\n\n        # only Alice\n        for t, i, j in edges:\n            if t == 1:\n                if uni(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n\n        # only Bob\n        root = root0\n        for t, i, j in edges:\n            if t == 2:\n                if uni(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if e1 == e2 == n - 1 else -1\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        graph ={}\n        for Type,start,end in edges:\n            if start not in graph:\n                graph[start]=[]\n            if end not in graph:\n                graph[end]=[]\n                \n            graph[start].append([Type,end])\n            graph[end].append([Type,start])\n        \n        \n        def dfs(node,p):\n            for Type,neighbour in graph[node]:\n                if Type in p:\n                    if neighbour not in visited:\n                        visited[neighbour]=True\n                        dfs(neighbour,p)\n                        \n            \n                    \n        \n        #check if connected\n        visited ={}\n        #dfs on 1,3,\n        dfs(1,[1,3])\n        if len(list(visited.keys()))!=n:\n            return -1\n    \n        visited ={}\n        dfs(1,[2,3])\n        if len(list(visited.keys()))!=n:\n            return -1\n        #dfs on 2,3\n        \n        \n        #find number of blue components:\n        visited ={}\n        #dfs on blue edges, \n        blue_cc=0\n        blue_edges =0\n        for Type,start,end in edges:\n            if Type==3:\n                \n                if start not in visited:\n                    temp = len(list(visited.keys()))\n                    blue_cc+=1\n                    dfs(start,[3])\n                    blue_edges+=len(list(visited.keys()))-temp-1\n                \n                if end not in visited:\n                    temp = len(list(visited.keys()))\n                    blue_cc+=1\n                    dfs(end,[3])\n                    blue_edges+=len(list(visited.keys()))-temp-1\n                \n                \n            \n                    \n        unvisited = len(list(graph.keys()))-len(list(visited.keys()))\n                \n            \n        #keep track of # of visitedNode -1\n        #number of times dfs is called\n        \n        #calc ans = totalnumber of edges - (blue edges + 2*unvisited nodes + 2*(blue components-1))\n        print((blue_edges,unvisited,blue_cc))\n        return len(edges)-(blue_edges+ 2*unvisited +2*(blue_cc-1))\n        \n        \n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        '''always keep type 3 if possible'''\n        edges123 = [[]  for _ in range(3) ] \n        for t, a, b in edges: edges123[t-1].append( (a-1,b-1) )\n        # type 0 1 2 for alice, bob and both\n        self.res = 0\n        Parents = [[i for i in range(n)] for _ in range(2) ]\n        depth = [[1]*n for _ in range(2)]\n        selectedEdges = [0,0]\n        def FindRoot(n,t):\n            #print('node',n,'type',t)\n            if Parents[t][n] != n:\n                Parents[t][n] = FindRoot(Parents[t][n] ,t)\n            return Parents[t][n] \n        def Uni(x,y,t):\n            rx, ry = FindRoot(x,t), FindRoot(y,t)\n            if rx == ry: return 0\n            else:\n                if depth[t][rx] >= depth[t][ry]:\n                    Parents[t][ry] = rx\n                    depth[t][rx] = max(depth[t][rx],depth[t][ry])\n                else:\n                    Parents[t][rx] = ry\n                return 1\n            \n        def connect(thetype):\n            mytypes = [thetype] if thetype < 2 else [ 0, 1 ]\n            for x, y in edges123[thetype]:\n                if all(Uni(x,y,t) for t in mytypes):\n                    for t in mytypes: selectedEdges[t] += 1\n                else:\n                    self.res += 1\n            # for t in mytypes: \n            #     root = [FindRoot(i,t) for i in range(n)]\n            #     print(thetype,t, 'parents',Parents[t],root,selectedEdges,self.res)\n                \n        connect(2)\n        connect(0)\n        connect(1)\n        return self.res if all(selectedEdges[t]==n-1 for t in [0,1]) else -1\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(p, u):\n            parent = p[u]\n            if parent == u:\n                return parent\n            \n            p[u] = find(p, parent)\n            return p[u]\n        \n        def union(p, rank, root_u, root_v):\n            if rank[root_u] < rank[root_v]:\n                p[root_u] = root_v\n            elif rank[root_v] < rank[root_u]:\n                p[root_v] = root_u\n            else:\n                p[root_u] = root_v\n                rank[root_v] += 1\n        \n        p = list(range(n))\n        rank = [0] * n\n        \n        full_edges = set()\n        partial_edges = set()\n        partial_adj = {}\n        partial_adj[1] = collections.defaultdict(set)\n        partial_adj[2] = collections.defaultdict(set)\n        for e in edges:\n            edge_type, u, v = e[0], e[1] - 1, e[2] - 1\n            if edge_type == 3:\n                full_edges.add((u, v))\n            else:\n                partial_edges.add((edge_type, u, v))\n                partial_adj[edge_type][u].add(v)\n                partial_adj[edge_type][v].add(u)\n\n        nb_edges_in_mst = 0\n        for e in full_edges:\n            u, v = e\n            root_u, root_v = find(p, u), find(p, v)\n            if root_u != root_v:\n                union(p, rank, root_u, root_v)\n                nb_edges_in_mst += 1\n        \n        for e in partial_edges:\n            edge_type, u, v = e\n            root_u, root_v = find(p, u), find(p, v)\n            if root_u == root_v:\n                continue\n\n            # We have two nodes u and v such that they fall into two disjoint\n            # connected sub-graphs, and u from subgraph A is connected to v\n            # in subgraph B with either edge_type == 1 or edge_type 2. Since we\n            # need to reach v from subgraph A by both Alice and Bob, if we can\n            # find another node, x, in subgraph A that is connected to v in subgraph B\n            # by the other edge_type, then we can reach v from any node in subgraph A.\n            needed_edge_type = 2 if edge_type == 1 else 2\n            foo = (v, root_u)\n            found_needed_edge = False\n            for x in partial_adj[needed_edge_type][foo[0]]:\n                root_x = find(p, x)\n                if root_x == foo[1]:\n                    # x is in in subgraph A, same as u, AND it's connected to v via the\n                    # needed_edge_type\n                    union(p, rank, root_x, foo[1])\n                    union(p, rank, root_u, root_v)\n                    nb_edges_in_mst += 2\n                    found_needed_edge = True\n                    break\n            if found_needed_edge:\n                continue\n            \n            foo = (u, root_v)\n            for x in partial_adj[needed_edge_type][foo[0]]:\n                root_x = find(p, x)\n                if root_x == foo[1]:\n                    # y is in the subgraph B, same as v, and it's connected to u via the\n                    # needed_edge_type\n                    union(p, rank, root_x, foo[1])\n                    union(p, rank, root_u, root_v)\n                    nb_edges_in_mst += 2\n                    break\n\n        uniq_roots = set()\n        for u in range(len(p)):\n            uniq_roots.add(find(p, u))\n        if len(uniq_roots) != 1:\n            return -1  \n        \n        return len(edges) - nb_edges_in_mst\n            \n                \n            \n        \n", "class UnionFind:\n    \\\"\\\"\\\"A minimalist standalone union-find implementation.\\\"\\\"\\\"\n    def __init__(self, n):\n        self.count = n               # number of disjoint sets \n        self.parent = list(range(n)) # parent of given nodes\n        self.rank = [1]*n            # rank (aka size) of sub-tree \n        \n    def find(self, p):\n        \\\"\\\"\\\"Find with path compression.\\\"\\\"\\\"\n        if p != self.parent[p]: \n            self.parent[p] = self.find(self.parent[p]) # path compression \n        return self.parent[p]\n    \n    def union(self, p, q):\n        \\\"\\\"\\\"Union with ranking.\\\"\\\"\\\"\n        prt, qrt = self.find(p), self.find(q)\n        if prt == qrt: return False\n        self.count -= 1 # one more connection => one less disjoint \n        if self.rank[prt] > self.rank[qrt]: \n            prt, qrt = qrt, prt # add small sub-tree to large sub-tree for balancing\n        self.parent[prt] = qrt\n        self.rank[qrt] += self.rank[prt] # ranking \n        return True\n    \n        \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges) -> int:\n        ufa = UnionFind(n) # for Alice\n        ufb = UnionFind(n) # for Bob\n        ans = 0\n        edges.sort(reverse=True) \n        for t, u, v in edges: \n            u, v = u-1, v-1\n            if t == 3: \n                ans += not (ufa.union(u, v) and ufb.union(u, v)) # Alice & Bob\n            elif t == 2: \n                ans += not ufb.union(u, v)                     # Bob only\n            else: \n                ans += not ufa.union(u, v)                            # Alice only\n        return ans if ufa.count == 1 and ufb.count == 1 else -1 # check if uf is connected ", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        p = [-1 for i in range(n+1)]\n        c = [1 for i in range(n+1)]\n\n        def find(i, p, c):\n            pi = p[i]\n            nc = []\n            nodes = []\n            total = 0\n            while pi != -1:\n                nodes.append(i)\n                nc.append(total)\n                total = c[i]\n                i = pi\n                pi = p[i]\n            for k, vi in enumerate(nodes):\n                p[vi] = i\n                c[vi] -= nc[k]\n            return i\n\n        def union(i, j, p, c):\n            si, sj = find(i, p, c), find(j, p, c)\n            if si == sj:\n                return si\n            if c[si] > c[sj]:\n                p[sj] = si\n                c[si] += c[sj]\n                return si\n            else:\n                p[si] = sj\n                c[sj] += c[si]\n                return sj\n            \n        # connected component\n        e1s = []\n        e2s = []\n        s = -1\n        for i, ed in enumerate(edges):\n            e, u, v = ed\n            if e == 1:\n                e1s.append(i)\n            elif e == 2:\n                e2s.append(i)\n            else:\n                ns = union(u, v, p, c)\n                if s == -1 or c[s] < c[ns]:\n                    s = ns\n        pvst = set()\n        num_edges = 0\n        for i in range(1, n+1):\n            si = find(i, p, c)\n            if si in pvst:\n                continue\n            pvst.add(si)\n            num_edges += c[si]-1\n        \n        def check(es):\n            np = p.copy()\n            nc = c.copy()\n            for i in es:\n                _, u, v = edges[i]\n                union(u, v, np, nc)\n            pset = {find(i, np, nc) for i in range(1, n+1)}\n            return len(pset) == 1\n        \n        if not check(e1s) or not check(e2s):\n            return -1\n        need = 2*(n-1) - num_edges\n        return len(edges) - need", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        for edge in edges:\n            edge[1] -= 1\n            edge[2] -= 1\n        \n        blueUset = self.Uset(n)\n        blueUsed = self.countUsed(n, [(edge[1], edge[2]) for edge in edges if edge[0] == 3], blueUset)\n        \n        for i in range(0, n):\n            blueUset.find(i)\n        \n        redUset = self.Uset(n)\n        redUset.parents = blueUset.parents[:]\n        redUset.gCounts = blueUset.gCounts\n        redUsed = self.countUsed(n, [(edge[1], edge[2]) for edge in edges if edge[0] == 1], redUset)\n        if redUset.gCounts > 1:\n            return -1\n        \n        greenUset = self.Uset(n)\n        greenUset.parents = blueUset.parents[:]\n        greenUset.gCounts = blueUset.gCounts\n        greenUsed = self.countUsed(n, [(edge[1], edge[2]) for edge in edges if edge[0] == 2], greenUset)\n        if greenUset.gCounts > 1:\n            return -1\n        \n        return len(edges) - len(blueUsed) - len(redUsed) - len(greenUsed)\n        \n        \n    def countUsed(self, n, edges, uset):\n        usedEdges = []\n        for edge in edges:\n            u = edge[0]\n            v = edge[1]\n            if uset.find(u) != uset.find(v):\n                usedEdges.append(edge)\n                uset.union(u, v)\n            \n        return usedEdges\n    \n    class Uset:\n        def __init__(self, n):\n            self.parents = [i for i in range(0, n)]\n            self.gCounts = n\n        \n        def find(self, x):\n            if self.parents[x] != x:\n                self.parents[x] = self.find(self.parents[x])\n            return self.parents[x]\n            \n        def union(self, x, y):\n            px = self.find(x)\n            py = self.find(y)\n            if px != py:\n                self.parents[px] = py\n                self.gCounts -= 1\n                \n            return\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # think it in another way as to construct minimum spanning trees for Alice and Bob\n        # if there are then all the rest of the edges can be removed\n        # prioritize type 3 edges\n        edges.sort(key=lambda edge: -edge[0])\n        ds_alice = DisjointSets(n)\n        ds_bob = DisjointSets(n)\n        edges_added = 0\n        for e_type, u, v in edges:\n            if e_type == 3:\n                edges_added += int(ds_alice.union(u - 1, v - 1) | ds_bob.union(u - 1, v - 1))\n            elif e_type == 2:\n                edges_added += int(ds_bob.union(u - 1, v - 1))\n            else:\n                edges_added += int(ds_alice.union(u - 1, v - 1))\n        return len(edges) - edges_added if ds_alice.isConnected() and ds_bob.isConnected() else -1\n        \n        \nclass DisjointSets:\n    def __init__(self, n: int) -> None:\n        self.parent = [x for x in range(n)]\n        self.set_size = n\n        \n    def union(self, x: int, y: int) -> bool:\n        x = self.find(x)\n        y = self.find(y)\n        if x != y:\n            self.parent[x] = y\n            self.set_size -= 1\n            return True\n        return False\n    \n    def find(self, x: int) -> int:\n        if x != self.parent[x]:\n            self.parent[x] = self.find(self.parent[x])\n        return self.parent[x]\n    \n    def isConnected(self) -> bool:\n        return self.set_size == 1", "class UnionFind:\n    def __init__(self, items):\n        self.leader = {}\n        for item in items:\n            self.leader[item] = item\n    \n    def union(self, i1, i2):\n        l1, l2 = self.find(i1), self.find(i2)\n        if l1 == l2:\n            return False\n        self.leader[l1] = self.leader[l2]\n        return True\n        \n    def find(self, i):\n        if self.leader[i] != i:\n            self.leader[i] = self.find(self.leader[i])\n        return self.leader[i]\n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \\\"\\\"\\\"\n        Check if A and B can traverse the entire graph at first\n        \\\"\\\"\\\"\n        items = [i for i in range(1, n + 1)]\n        uf1 = UnionFind(items)\n        uf2 = UnionFind(items)\n        \n        a_essential_edges = 0\n        b_essential_edges = 0\n        removable_edges = 0\n        \n        for [t, u, v] in edges:\n            if t == 3:\n                union_success = uf1.union(u, v)\n                uf2.union(u, v)\n                \n                if union_success:\n                    a_essential_edges += 1\n                    b_essential_edges += 1\n                else:\n                    removable_edges += 1\n                    \n        for [t, u, v] in edges:\n            if t == 1:\n                union_success = uf1.union(u, v)\n                if union_success:\n                    a_essential_edges += 1\n                else:\n                    removable_edges += 1\n        for [t, u, v] in edges:\n            if t == 2:\n                union_success = uf2.union(u, v)\n                if union_success:\n                    b_essential_edges += 1\n                else:\n                    removable_edges += 1\n        \n        \n        \n        return removable_edges if a_essential_edges == b_essential_edges == n - 1 else -1\n        ", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(p, u):\n            parent = p[u]\n            if parent == u:\n                return parent\n            \n            p[u] = find(p, parent)\n            return p[u]\n        \n        def union(p, rank, root_u, root_v):\n            if rank[root_u] < rank[root_v]:\n                p[root_u] = root_v\n            elif rank[root_v] < rank[root_u]:\n                p[root_v] = root_u\n            else:\n                p[root_u] = root_v\n                rank[root_v] += 1\n        \n        p = list(range(n))\n        rank = [0] * n\n        \n        full_edges = set()\n        partial_edges = set()\n        partial_adj = {}\n        partial_adj[1] = collections.defaultdict(set)\n        partial_adj[2] = collections.defaultdict(set)\n        for e in edges:\n            edge_type, u, v = e[0], e[1] - 1, e[2] - 1\n            if edge_type == 3:\n                full_edges.add((u, v))\n            else:\n                partial_edges.add((edge_type, u, v))\n                partial_adj[edge_type][u].add(v)\n                partial_adj[edge_type][v].add(u)\n\n        nb_edges_in_mst = 0\n        for e in full_edges:\n            u, v = e\n            root_u, root_v = find(p, u), find(p, v)\n            if root_u != root_v:\n                union(p, rank, root_u, root_v)\n                nb_edges_in_mst += 1\n        \n        for e in partial_edges:\n            edge_type, u, v = e\n            root_u, root_v = find(p, u), find(p, v)\n            if root_u == root_v:\n                continue\n\n            # We have two nodes u and v such that they fall into two disjoint\n            # connected sub-graphs, and u from subgraph A is connected to v\n            # in subgraph B with either edge_type == 1 or edge_type 2. Since we\n            # need to reach v from subgraph A by both Alice and Bob, if we can\n            # find another node, x, in subgraph A that is connected to v in subgraph B\n            # by the other edge_type, then we can reach v from any node in subgraph A.\n            needed_edge_type = 2 if edge_type == 1 else 2\n            found_needed_edge = False\n            for x in partial_adj[needed_edge_type][v]:\n                root_x = find(p, x)\n                if root_x == root_u:\n                    # x is in in subgraph A, same as u, AND it's connected to v via the\n                    # needed_edge_type\n                    union(p, rank, root_x, root_u)\n                    union(p, rank, root_u, root_v)\n                    nb_edges_in_mst += 2\n                    found_needed_edge = True\n                    break\n            if found_needed_edge:\n                continue\n                \n            for y in partial_adj[needed_edge_type][u]:\n                root_y = find(p, y)\n                if root_y == root_v:\n                    # y is in the subgraph B, same as v, and it's connected to u via the\n                    # needed_edge_type\n                    union(p, rank, root_y, root_u)\n                    union(p, rank, root_u, root_v)\n                    nb_edges_in_mst += 2\n                    break\n        \n        uniq_roots = set()\n        for u in range(len(p)):\n            uniq_roots.add(find(p, u))\n        if len(uniq_roots) != 1:\n            return -1  \n        \n        return len(edges) - nb_edges_in_mst\n            \n                \n            \n        \n", "class DSU:\n    def __init__(self, n):\n        self.parent = [x for x in range(n)]\n        self.rank = [0] * n\n    \n    def find(self, x):\n        if self.parent[x] != x:\n            self.parent[x] = self.find(self.parent[x])\n        return self.parent[x]\n    \n    def union(self, x, y):\n        px, py = self.find(x), self.find(y)\n        if px == py:\n            return False\n        else:\n            if self.rank[px] > self.rank[py]:\n                self.parent[py] = px\n            elif self.rank[py] > self.rank[px]:\n                self.parent[px] = py\n            else:\n                self.parent[px] = py\n                self.rank[px] += 1\n            return True\n        \n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        dsu1, dsu2 = DSU(n), DSU(n)\n        ans = 0\n        for t, u, v in edges:\n            if t == 3:\n                if not dsu1.union(u - 1, v - 1) or not dsu2.union(u - 1, v - 1):\n                    ans += 1\n        \n        for t, u, v in edges:\n            if t == 1 and not dsu1.union(u - 1, v - 1):\n                ans += 1\n            if t == 2 and not dsu2.union(u - 1, v - 1):\n                ans += 1\n        \n        p1, p2 = dsu1.find(0), dsu2.find(0)\n        for i in range(n):\n            if p1 != dsu1.find(i) or p2 != dsu2.find(i):\n                return -1\n        return ans", "from typing import List\nclass Solution:\n    def find(self,i):\n        if i != self.root[i]:\n            self.root[i] = self.find(self.root[i])\n        return self.root[i]\n\n    def uni(self,x, y):\n        x, y = self.find(x), self.find(y)\n        if x == y: return 0\n        self.root[x] = y\n        return 1\n    def maxNumEdgesToRemove(self, n, edges):\n        # Union find\n\n        res = e1 = e2 = 0\n\n        # Alice and Bob\n        self.root = list(range(n + 1))\n        for t, i, j in edges:\n            if t == 3:\n                if self.uni(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        root0 = self.root[:]\n\n        # only Alice\n        for t, i, j in edges:\n            if t == 1:\n                if self.uni(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n\n        # only Bob\n        self.root = root0\n        for t, i, j in edges:\n            if t == 2:\n                if self.uni(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if e1 == e2 == n - 1 else -1", "class UF:\n    def __init__(self):\n        self.d = defaultdict(int)\n        \n    def findRoot(self, key):\n        if self.d[key] > 0:\n            self.d[key] = self.findRoot(self.d[key])\n            return self.d[key]\n        else:\n            return key\n        \n    def mergeRoot(self, k1, k2):\n        r1, r2 = self.findRoot(k1), self.findRoot(k2)  \n        if r1 != r2:\n            r1, r2 = min(r1, r2), max(r1, r2)\n            self.d[r1] += self.d[r2]\n            self.d[r2] = r1\n        return r1\n    \n\nimport heapq\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        t12, t3 = [], []\n        for e in edges:\n            if e[0] == 3:\n                t3.append(e)\n            else:\n                t12.append(e)\n                \n        uf1 = UF()\n        uf2 = UF()\n        \n        ttl = 0\n        for e in t3:\n            if uf1.findRoot(e[1]) != uf1.findRoot(e[2]) or uf2.findRoot(e[1]) != uf2.findRoot(e[2]):\n                uf1.d[uf1.mergeRoot(e[1], e[2])] -= 1\n                uf2.d[uf2.mergeRoot(e[1], e[2])] -= 1\n            else:\n                ttl += 1   \n                    \n        for e in t12:\n            if e[0] == 1 and uf1.findRoot(e[1]) != uf1.findRoot(e[2]):\n                uf1.d[uf1.mergeRoot(e[1], e[2])] -= 1\n            elif e[0] == 2 and uf2.findRoot(e[1]) != uf2.findRoot(e[2]):\n                uf2.d[uf2.mergeRoot(e[1], e[2])] -= 1\n            else:\n                ttl += 1\n                 \n        if uf1.d[1] != - n + 1 or uf2.d[1] != - n + 1:\n            return -1\n        \n        return ttl", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        class Union:\n            \n            def __init__(self):\n                \n                self.parent = -1\n                self.rank = 1\n                \n            def Find(self):\n                \n                if self.parent == -1: return self\n                return self.parent.Find()\n            \n            def UNION(self, node):\n                \n                L, R = self.Find(), node.Find()\n                if L == R: return 0\n                elif L.rank > R.rank: R.parent = L\n                elif R.rank > L.rank: L.parent = R\n                else:\n                    L.parent = R\n                    R.rank += 1\n                    \n                return 1\n        \n    \n        alice, bob = [], []\n    \n        for t, u, v in edges:\n            if t == 3:\n                alice.append([t, u, v])\n                bob.append([t, u, v])\n                \n        for t, u, v in edges:\n            if t == 1:\n                alice.append([t, u, v])\n            elif t == 2:\n                bob.append([t, u, v])\n            \n                \n        Vertex = {}\n        for i in range(n): Vertex[i + 1] = Union()\n            \n        Count, Common = 0, 0\n        \n        for t, u, v in alice:\n            \n            if Vertex[u].UNION(Vertex[v]) == 1: \n                Count += 1\n                if t == 3: Common += 1\n    \n        if Count < n - 1: return -1\n        \n        for u in Vertex: Vertex[u].parent = -1\n        \n        Count = 0\n        \n        for t, u, v in bob:\n            \n            if Vertex[u].UNION(Vertex[v]) == 1: \n                Count += 1\n                \n        if Count < n - 1: return -1\n        \n        return len(edges) - Common - 2*(n - 1 - Common)\n        \n            \n        \n        \n        \n        \n        \n        \n        \n                \n                \n                \n                \n\n                \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        elements = [-1] * (n + 1)\n        elements[0] = 0\n        elements2 = None\n        \n        def find(elements, i):\n            while elements[i] >= 0:\n                i = elements[i]\n            return i\n        \n        def union(elements, i, j):\n            i = find(elements, i)\n            j = find(elements, j)\n            if i == j:\n                return False\n            if elements[i] <= elements[j]:\n                if elements[i] == elements[j]:\n                    elements[i] -= 1\n                elements[j] = i\n            else:\n                elements[i] = j\n            return True\n        \n        def count(elements):\n            return sum(1 for i in elements if i < 0)\n        edges.sort(key=lambda k: k[0])\n        result = 0\n        for t, u, v in reversed(edges):\n            if t == 3:\n                if not union(elements, u, v):\n                    result += 1\n            else:\n                if elements2 is None:\n                    elements2 = elements[:]\n                if t == 2:\n                    if not union(elements2, u, v):\n                        result += 1\n                elif t == 1:\n                    if not union(elements, u, v):\n                        result += 1\n        if count(elements) > 1 or (elements2 is not None and count(elements2)) > 1:\n            return -1\n        return result", "# from collections import defaultdict\n\n# class Solution:\n#     def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n#         aj = [defaultdict(set) for i in range(4)]\n#         total = len(edges)\n#         for t, i, j in edges:\n#             if i == j:\n#                 continue\n#             aj[t][i].add(j)\n#             aj[t][j].add(i)\n        \n#         used = set()\n#         visited = {1}\n#         t3 = [(1, i) for i in aj[3][1]]\n#         t1 = [(1, i) for i in aj[1][1]]\n#         while len(visited) < n and (t3 or t1):\n#             if t3:\n#                 i, j = t3.pop()\n#                 reusable = True\n#             else:\n#                 i, j = t1.pop()\n#                 reusable = False\n#             if j in visited:\n#                 continue\n                \n#             if reusable:\n#                 used.add((min(i, j), max(i, j)))\n#             visited.add(j)\n#             for k in aj[3][j]:\n#                 if k not in visited:\n#                     t3.append((j, k))\n#             for k in aj[1][j]:\n#                 if k not in visited:\n#                     t1.append((j, k))\n#         if len(visited) < n:\n#             return -1\n            \n#         reused = set()\n#         visited = {1}\n#         t0 = []\n#         t2 = [(1, i) for i in aj[2][1]]\n#         for i in aj[3][1]:\n#             if (1, i) in used:\n#                 t0.append((1, i))\n#             else:\n#                 t2.append((1, i))\n#         while len(visited) < n and (t0 or t2):\n#             if t0:\n#                 i, j = t0.pop()\n#                 reusable = True\n#             else:\n#                 i, j = t2.pop()\n#                 reusable = False\n#             if j in visited:\n#                 continue\n                \n#             if reusable:\n#                 reused.add((min(i, j), max(i, j)))\n#             visited.add(j)\n#             for k in aj[3][j]:\n#                 if k not in visited:\n#                     if (min(j, k), max(j, k)) in used:\n#                         t0.append((j, k))\n#                     else:\n#                         t2.append((j, k))\n#             for k in aj[2][j]:\n#                 if k not in visited:\n#                     t2.append((j, k))\n#         if len(visited) < n:\n#             return -1\n\n#         return total - ((n - 1) * 2 - len(reused))\n\n\nfrom collections import defaultdict\nfrom heapq import heappush, heappop\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        aj = [defaultdict(set) for i in range(4)]\n        total = len(edges)\n        for t, i, j in edges:\n            if i == j:\n                continue\n            aj[t][i].add(j)\n            aj[t][j].add(i)\n        \n        reuse = set()\n        count = 0\n        \n        visited = {1}\n        heap = []\n        for i in aj[3][1]:\n            heappush(heap, (1, 1, i))\n        for i in aj[1][1]:\n            heappush(heap, (2, 1, i))\n        while len(visited) < n and heap:\n            w, i, j = heappop(heap)\n            if j in visited:\n                continue\n                \n            if w == 1:\n                reuse.add((i, j))\n            count += 1\n            visited.add(j)\n            for k in aj[3][j]:\n                if k not in visited:\n                    heappush(heap, (1, j, k))\n            for k in aj[1][j]:\n                if k not in visited:\n                    heappush(heap, (2, j, k))\n        if len(visited) < n:\n            return -1\n            \n        visited = {1}\n        heap = []\n        for i in aj[3][1]:\n            if (1, i) in reuse or (i, 1) in reuse:\n                heappush(heap, (0, 1, i))\n            else:\n                heappush(heap, (1, 1, i))\n        for i in aj[2][1]:\n            heappush(heap, (2, 1, i))\n        while len(visited) < n and heap:\n            w, i, j = heappop(heap)\n            if j in visited:\n                continue\n                \n            if w > 0:\n                count += 1\n            visited.add(j)\n            for k in aj[3][j]:\n                if k not in visited:\n                    if (j, k) in reuse or (k, j) in reuse:\n                        heappush(heap, (0, j, k))\n                    else:\n                        heappush(heap, (1, j, k))\n            for k in aj[2][j]:\n                if k not in visited:\n                    heappush(heap, (2, j, k))\n        if len(visited) < n:\n            return -1\n\n        return total - count\n", "class Solution:\n    \n    def find(self, c, parents):\n        s = c\n        while parents[c-1] != -1:\n            c = parents[c-1]\n        if s != c:\n            parents[s-1] = c\n        return c\n    \n    def delete_cycles(self, edges, parents, delete, t):\n        for edge in edges:\n            c1 = edge[0]\n            c2 = edge[1]\n            p1 = self.find(c1, parents)\n            p2 = self.find(c2, parents)\n            # print(f'edge:    {edge}')\n            # print(f'p1  p2:  {p1} {p2}')\n            # print(f'type:    {t}')\n            # print(f'parents: {parents}')\n            # print()\n            if p1 == p2:\n                delete.add((t, c1, c2))\n            else:\n                parents[p1-1] = p2\n        \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        common = [(e[1], e[2]) for e in edges if e[0] == 3]\n        type1 = [(e[1], e[2]) for e in edges if e[0] == 1]\n        type2 = [(e[1], e[2]) for e in edges if e[0] == 2]\n        \n        delete = set()\n        \n        parents1 = [-1] * n\n        parents2 = [-1] * n\n        \n        self.delete_cycles(common, parents1, delete, 3)\n        self.delete_cycles(type1, parents1, delete, 1)\n        # print(\\\"-----------------------------------------------\\\")\n        self.delete_cycles(common, parents2, delete, 3)\n        self.delete_cycles(type2, parents2, delete, 2)\n        \n        has_single_parent1 = False\n        for p in parents1:\n            if p == -1:\n                if not has_single_parent1:\n                    has_single_parent1 = True\n                else:\n                    return -1\n                \n        has_single_parent2 = False\n        for p in parents2:\n            if p == -1:\n                if not has_single_parent2:\n                    has_single_parent2 = True\n                else:\n                    return -1\n                \n        return len(delete)\n                \n                \n        \n        \n                \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        '''always keep type 3 if possible'''\n        edges123 = [collections.defaultdict(list)  for _ in range(3) ] \n        for t, a, b in edges: edges123[t-1][a-1].append(b-1)\n        # type 0 1 2 for alice, bob and both\n        self.res = 0\n        Parents = [[i for i in range(n)] for _ in range(2) ]\n        selectedEdges = [0,0]\n        def FindRoot(n,t):\n            #print('node',n,'type',t)\n            if Parents[t][n] != n:\n                Parents[t][n] = FindRoot(Parents[t][n] ,t)\n            return Parents[t][n] \n        def Uni(x,y,t):\n            x, y = FindRoot(x,t), FindRoot(y,t)\n            if x == y: return 0\n            else:\n                Parents[t][x] = y\n                return 1\n            \n        def connect(thetype):\n            mytypes = [thetype] if thetype < 2 else [ 0, 1 ]\n            for node in range(n):\n                for neighbor in edges123[thetype][node]:\n                    if all(Uni(neighbor,node,t) for t in mytypes):\n                        for t in mytypes: selectedEdges[t] += 1\n                    else:\n                        self.res += 1\n            # for t in mytypes: \n            #     root = [FindRoot(i,t) for i in range(n)]\n            #     print(thetype,t, 'parents',Parents[t],root,selectedEdges,self.res)\n                \n        connect(2)\n        connect(0)\n        connect(1)\n        return self.res if all(selectedEdges[t]==n-1 for t in [0,1]) else -1\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        parent = [[i for i in range(n+1)] for _ in range(4)]\n        size = [[1 for i in range(n+1)] for _ in range(4)]\n        \n        def find(x, t):\n            if parent[t][x] != x:\n                parent[t][x] = find(parent[t][x], t)\n            return parent[t][x]\n        \n        def union(x, y, t):\n            xs, ys = find(x, t), find(y, t)\n            if xs == ys: return False\n            if size[t][xs] < size[t][ys]:\n                xs, ys = ys, xs\n            size[t][xs] += size[t][ys]\n            parent[t][ys] = xs\n            return True\n        \n        ans = 0\n        for t, u, v in edges:\n            if t != 3: continue\n            union(u, v, 1)\n            union(u, v, 2)\n            if not union(u, v, 3): ans += 1\n                \n        for t, u, v in edges:\n            if t != 1: continue\n            if not union(u, v, 1): ans += 1\n        \n        for t, u, v in edges:\n            if t != 2: continue\n            if not union(u, v, 2): ans += 1\n                \n        for i in range(1, n+1):\n            for t in [1, 2]:\n                if size[t][find(i, t)] != n:\n                    return -1\n        \n        return ans\n        \n        \n        \n        \n        \n        \n        \n        \n", "class UF:\n    def __init__(self):\n        self.d = defaultdict(int)\n        \n    def findRoot(self, key):\n        if self.d[key] > 0:\n            self.d[key] = self.findRoot(self.d[key])\n            return self.d[key]\n        else:\n            return key\n        \n    def mergeRoot(self, k1, k2):\n        r1, r2 = self.findRoot(k1), self.findRoot(k2)  \n        if r1 != r2:\n            r1, r2 = min(r1, r2), max(r1, r2)\n            self.d[r1] += self.d[r2]\n            self.d[r2] = r1\n        return r1\n    \n\nimport heapq\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        t12, t3 = [], []\n        for e in edges:\n            if e[0] == 3:\n                t3.append(e)\n            else:\n                t12.append(e)\n                \n        uf1 = UF()\n        uf2 = UF()\n        uf1.d[1] = -1\n        uf2.d[1] = -1\n        ttl = 0\n        for e in t3:\n            if uf1.findRoot(e[1]) != uf1.findRoot(e[2]) or uf2.findRoot(e[1]) != uf2.findRoot(e[2]):\n                uf1.d[uf1.mergeRoot(e[1], e[2])] -= 1\n                uf2.d[uf2.mergeRoot(e[1], e[2])] -= 1\n                ttl += 1   \n                    \n        for e in t12:\n            if e[0] == 1 and uf1.findRoot(e[1]) != uf1.findRoot(e[2]):\n                uf1.d[uf1.mergeRoot(e[1], e[2])] -= 1\n            elif e[0] == 2 and uf2.findRoot(e[1]) != uf2.findRoot(e[2]):\n                uf2.d[uf2.mergeRoot(e[1], e[2])] -= 1\n                 \n        if uf1.d[1] != - n or uf2.d[1] != - n:\n            return -1\n        \n        return len(edges) - 2 * n + 2 + ttl", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        edges = sorted(edges, reverse=True)\n        alice, bob = [i for i in range(n + 1)], [i for i in range(n + 1)]\n        size_alice, size_bob = [1 for i in range(n + 1)], [1 for i in range(n + 1)]\n        ans = 0\n        \n        def find(u, state):\n            if state == \\\"a\\\":\n                if alice[u] == u:\n                    return u\n                return find(alice[u], state)\n            else:\n                if bob[u] == u:\n                    return u\n                return find(bob[u], state)\n            \n        \n        def union(u, v, state):\n            nonlocal ans\n            \n            add = 0\n            if state == 3 or state == 1:\n                p1, p2 = find(u, \\\"a\\\"), find(v, \\\"a\\\")\n                if p1 != p2:\n                    add = 1\n                    print(\\\"haha\\\", u, v)\n                    if size_alice[p1] >= size_alice[p2]:\n                        size_alice[p1] += size_alice[p2]\n                        alice[p2] = p1\n                    else:\n                        size_alice[p2] += size_alice[p1]\n                        alice[p1] = p2\n\n            if state == 3 or state == 2:\n                p1, p2 = find(u, \\\"b\\\"), find(v, \\\"b\\\")\n                if p1 != p2:\n                    add = 1\n                    print(\\\"haha\\\", u, v)\n                    if size_bob[p1] >= size_bob[p2]:\n                        size_bob[p1] += size_bob[p2]\n                        bob[p2] = p1\n                    else:\n                        size_bob[p2] += size_bob[p1]\n                        bob[p1] = p2\n            ans += add\n                \n        \n        for t,u,v in edges:\n            union(u, v, t)\n        # print(size_alice, size_bob)\n        if max(size_alice) != n or max(size_bob) != n:\n            return -1\n        return len(edges) - ans", "class Solution:\n    def span(self,A,n,flag,par):\n        countt=0\n        countu=0\n        for i,(t,u,v) in enumerate(A):\n            if t==flag:\n                countt+=1\n                p1=u\n                while par[p1]>0:\n                    p1=par[p1]\n                p2=v\n                while par[p2]>0:\n                    p2=par[p2]\n                    \n                if p1==p2:\n                    continue\n                    \n                else:\n                    countu+=1\n                    if abs(par[p1])>abs(par[p2]):\n                        par[p1]-=par[p2]\n                        if u!=p1:\n                            par[u]=p1\n                        par[v]=p1\n                        par[p2]=p1\n                    else:\n                        par[p2]-=par[p1]\n                        if v!=p2:\n                            par[v]=p2\n                        par[u]=p2\n                        par[p1]=p2\n         \n        return countt,countu\n        \n    \n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        par=[-1]*(n+1)\n        ct,cu=self.span(edges,n,3,par)\n        if cu==n-1:\n            return len(edges)-cu\n        copy=par[:]\n        ct2,cu2=self.span(edges,n,2,par)\n        ct1,cu1=self.span(edges,n,1,copy)\n        if cu+cu2!=n-1 or cu+cu1!=n-1:\n            return -1\n        \n        return len(edges)-(cu+cu1+cu2)\n        \n        \n        \n        \n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        '''always keep type 3 if possible'''\n        edges123 = [collections.defaultdict(list)  for _ in range(3) ] \n        for t, a, b in edges: edges123[t-1][a-1].append(b-1)\n        # type 0 1 2 for alice, bob and both\n        self.res = 0\n        Parents = [[i for i in range(n)] for _ in range(2) ]\n        selectedEdges = [0,0]\n        def FindRoot(n,t):\n            #print('node',n,'type',t)\n            if Parents[t][n] != n:\n                Parents[t][n] = FindRoot(Parents[t][n] ,t)\n            return Parents[t][n] \n        def Uni(x,y,t):\n            rx, ry = FindRoot(x,t), FindRoot(y,t)\n            if rx == ry: return 0\n            else:\n                Parents[t][rx] = y\n                return 1\n            \n        def connect(thetype):\n            mytypes = [thetype] if thetype < 2 else [ 0, 1 ]\n            for node in range(n):\n                for neighbor in edges123[thetype][node]:\n                    if all(Uni(node,neighbor,t) for t in mytypes):\n                        for t in mytypes: selectedEdges[t] += 1\n                    else:\n                        self.res += 1\n            # for t in mytypes: \n            #     root = [FindRoot(i,t) for i in range(n)]\n            #     print(thetype,t, 'parents',Parents[t],root,selectedEdges,self.res)\n                \n        connect(2)\n        connect(0)\n        connect(1)\n        return self.res if all(selectedEdges[t]==n-1 for t in [0,1]) else -1\n", "class UF():\n    def __init__(self,n):\n        self.parent = list(range(n))\n    def find(self,p):\n        if self.parent[p] != p:\n            self.parent[p] = self.find (self.parent[p])\n        return self.parent[p]\n    def union(self,p,q):\n        pr = self.find(p)\n        qr = self.find(q)\n        if pr == qr:\n            return False\n        else:\n            self.parent[pr] = qr\n            return True\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        ufA, ufB, ufAB = UF(n),UF(n),UF(n)\n        usefulAB = 0\n        for edge in edges:\n            t = edge[0]\n            x = edge[1]\n            y = edge[2]  \n            if t == 1:\n                ufA.union(x-1, y -1)\n            elif t == 2:\n                ufB.union(x-1, y -1)\n            else:\n                ufA.union(x-1, y -1)\n                ufB.union(x-1, y -1)\n                usefulAB += ufAB.union(x-1, y -1)\n        \n        if len([i for i in range(n) if ufA.parent[i] == i]) > 1 or len([i for i in range(n) if ufB.parent[i] == i]) > 1:\n            return -1\n        return len(edges) - (2 * (n - 1) - usefulAB)\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        elements = [-1] * (n + 1)\n        elements[0] = 0\n        def find(elements, i):\n            while elements[i] >= 0:\n                i = elements[i]\n            return i\n        \n        def union(elements, i, j):\n            i = find(elements, i)\n            j = find(elements, j)\n            if i == j:\n                return\n            if elements[i] <= elements[j]:\n                if elements[i] == elements[j]:\n                    elements[i] -= 1\n                elements[j] = i\n            else:\n                elements[i] = j\n        \n        def count(elements):\n            return sum(1 for i in elements if i < 0)\n        \n        result = 0\n        for t, u, v  in edges:\n            if t != 3:\n                continue\n            if find(elements, u) == find(elements, v):\n                result += 1\n            else:\n                union(elements, u, v)\n        elements2 = elements[:]\n        for t, u, v in edges:\n            if t == 1:\n                if find(elements, u) == find(elements, v):\n                    result += 1\n                else:\n                    union(elements, u, v)\n            elif t == 2:\n                if find(elements2, u) == find(elements2, v):\n                    result += 1\n                else:\n                    union(elements2, u, v)\n        if count(elements) > 1 or count(elements2) > 1:\n            return -1\n        return result", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        A, B, ans, m = list(), list(), 0, len(edges)\n        ta, tb = set(), set()\n        for i, (t, u, v) in enumerate(sorted(edges, reverse=True)):\n            if t != 3:\n                if (u, v, 3) in ta or (u, v, 3) in tb:\n                    ans += 1\n                    m -= 1\n                elif t == 1:\n                    ta.add((u, v, t))\n                    A.append((u, v, i))\n                else:\n                    tb.add((u, v, t))\n                    B.append((u, v, i))\n            else:\n                ta.add((u, v, t))\n                A.append((u, v, i))\n                tb.add((u, v, t))\n                B.append((u, v, i))\n\n        def mst(edges):\n            p = list(range(n+1))\n            ret = set()\n            def find(x):\n                if x != p[x]:\n                    p[x] = find(p[x])\n                return p[x]\n            for u, v, i in edges:\n                pu, pv = find(u), find(v)\n                if pu != pv:\n                    ret.add(i)\n                    p[pu] = pv\n            return ret if len(ret) == n-1 else None\n        ta = mst(A)\n        if ta is None:\n            return -1\n        tb = mst(B)\n        if tb is None:\n            return -1\n        return ans + m - len(ta|tb)", "class Solution:\n    \n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        edge_type = {1:True, 2:False, 3:True}\n        self.father = [i for i in range(n)]\n        self.count = n\n        self.travel(n, edges, edge_type)\n        count_a = self.count\n        \n        edge_type = {1:False, 2:True, 3:True}\n        self.father = [i for i in range(n)]\n        self.count = n\n        self.travel(n, edges, edge_type)\n        count_b = self.count\n        \n        if count_a > 1 or count_b > 1:\n            return -1\n        \n        edge_type = {1:False, 2:False, 3:True}\n        self.father = [i for i in range(n)]\n        self.count = n\n        self.travel(n, edges, edge_type)\n        count_both = self.count\n        \n        delete_both = len([i for i in edges if i[0] in [3]]) - (n - count_both)\n        delete_a = len([i for i in edges if i[0] in [1, 3]]) - (n - 1) \n        delete_b = len([i for i in edges if i[0] in [2, 3]]) - (n - 1) \n        \n        return delete_a + delete_b - delete_both\n        \n        \n    \n    def travel(self, n, edges, edge_type):\n        \n        for edge in edges:\n            if edge_type[edge[0]]:\n                self.union(edge[1] - 1, edge[2] - 1)\n\n             \n        \n    def find(self, idx):\n        if self.father[idx] == idx:\n            return idx\n        else:\n            self.father[idx] = self.find(self.father[idx])\n            return self.father[idx]\n                    \n                    \n    def union(self, idx1, idx2):\n        father_a = self.find(idx1)\n        father_b = self.find(idx2)\n        if father_a != father_b:\n            self.count -= 1\n            self.father[father_a] = father_b\n", "class Solution:\n\\tdef maxNumEdgesToRemove(self, n: int, edges: [[int]]) -> int:\n\\t\\tdef find(arr: [int], x: int) -> int: \n\\t\\t\\tif arr[x] != x:\n\\t\\t\\t\\tarr[x] = find(arr, arr[x])\n\\t\\t\\treturn arr[x]\n\n\\t\\tdef union(arr: [int], x: int, y: int) -> int:\n\\t\\t\\tpx, py = find(arr, x), find(arr, y)\n\\t\\t\\tif px == py:\n\\t\\t\\t\\treturn 0\n\\t\\t\\tarr[px] = py\n\\t\\t\\treturn 1\n\n\\t\\tarr = [i for i in range(n+1)]\n\\t\\ta, b, ans = 0, 0, 0\n\\t\\tfor t, x, y in edges:\n\\t\\t\\tif t == 3:\n\\t\\t\\t\\tif union(arr, x, y):\n\\t\\t\\t\\t\\ta += 1\n\\t\\t\\t\\t\\tb += 1\n\\t\\t\\t\\telse:\n\\t\\t\\t\\t\\tans += 1\n\n\\t\\ttmp = arr[:]\n\\t\\tfor t, x, y in edges:\n\\t\\t\\tif t == 1:\n\\t\\t\\t\\tif union(arr, x, y):\n\\t\\t\\t\\t\\ta += 1\n\\t\\t\\t\\telse:\n\\t\\t\\t\\t\\tans += 1\n\n\\t\\tarr = tmp\n\\t\\tfor t, x, y in edges:\n\\t\\t\\tif t == 2:\n\\t\\t\\t\\tif union(arr, x, y):\n\\t\\t\\t\\t\\tb += 1\n\\t\\t\\t\\telse:\n\\t\\t\\t\\t\\tans += 1\n\n\\t\\treturn ans if a == b == n-1 else -1", "from collections import deque\n\nclass UnionFind:\n    def __init__(self, nodes):\n        self.parent = { n: None for n in nodes }\n        self.size = { n: 1 for n in nodes }\n        \n    def find_parent(self, node):\n        path = []\n        \n        while self.parent[node] is not None:\n            path.append(node)\n            node = self.parent[node]\n        \n        for n in path:\n            self.parent[n] = node\n            \n        return node\n    \n    def connected(self, a, b):\n        return self.find_parent(a) == self.find_parent(b)\n    \n    def connect(self, a, b):\n        a = self.find_parent(a)\n        b = self.find_parent(b)\n        \n        if a != b:\n            if self.size[a] > self.size[b]:\n                self.parent[b] = a\n                self.size[a] += self.size[b]\n            else:\n                self.parent[a] = b\n                self.size[b] += self.size[a]\n        \ndef min_spanning_tree(uf, nodes, edges):\n    result = []\n    for e in edges:\n        t, a, b = tuple(e)\n        \n        if uf.connected(a, b):\n            continue\n        else:\n            uf.connect(a, b)\n            result.append(e)\n    return result\n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        nodes = list(range(1, n+1))\n        uf_alice = UnionFind(nodes)\n        uf_bob = UnionFind(nodes)\n        \n        common_edges = min_spanning_tree(uf_alice, nodes, [e for e in edges if e[0] == 3])\n        min_spanning_tree(uf_bob, nodes, [e for e in edges if e[0] == 3])\n        \n        alice_edges = min_spanning_tree(uf_alice, nodes, [e for e in edges if e[0] == 1])\n        bob_edges = min_spanning_tree(uf_bob, nodes, [e for e in edges if e[0] == 2])\n        \n        if uf_alice.size[uf_alice.find_parent(1)] < n or uf_bob.size[uf_bob.find_parent(1)] < n:\n            return -1\n        \n        return len(edges) - (len(common_edges) + len(alice_edges) + len(bob_edges))", "class UnionFind:\n    def __init__(self, n):\n        self.p = {}\n        self.group = n\n        for i in range(1, n+1):\n            self.p[i] = i\n            \n\n    def unite(self, a, b):    \n        pa, pb = self.find(a), self.find(b)\n        if pa != pb:\n            self.p[pa] = pb\n            self.group -= 1\n            return True\n        return False\n\n    def find(self, a):\n        if self.p[a] != a:\n            self.p[a] = self.find(self.p[a]);\n        return self.p[a]\n\n    def united(self):\n        return self.group == 1\n    \nclass Solution:\n    # copied https://leetcode.com/problems/remove-max-number-of-edges-to-keep-graph-fully-traversable/discuss/831506/Textbook-Union-Find-Data-Structure-Code-with-Explanation-and-comments\n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges = sorted(edges, reverse=True)\n        \n        edgesAdded = 0        \n        bob, alice = UnionFind(n), UnionFind(n)        \n        for edge in edges:\n            tp, one, two = edge[0], edge[1], edge[2]\n            if tp == 3:\n                bu = bob.unite(one, two)\n                au = alice.unite(one, two)\n                edgesAdded += 1 if bu or au else 0\n            elif tp == 2:\n                edgesAdded += bob.unite(one, two)\n            else:\n                edgesAdded += alice.unite(one, two)\n\n        return len(edges)-edgesAdded if bob.united() and alice.united() else -1\n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        '''always keep type 3 if possible'''\n        edges123 = [collections.defaultdict(list)  for _ in range(3) ] \n        for t, a, b in edges: edges123[t-1][a-1].append(b-1)\n        # type 0 1 2 for alice, bob and both\n        self.res = 0\n        Parents = [[i for i in range(n)] for _ in range(2) ]\n        sizes = [[1]*n for _ in range(2)]\n        selectedEdges = [0,0]\n        def FindRoot(n,t):\n            #print('node',n,'type',t)\n            if Parents[t][n] != n:\n                Parents[t][n] = FindRoot(Parents[t][n] ,t)\n            return Parents[t][n] \n        def Uni(x,y,t):\n            rx, ry = FindRoot(x,t), FindRoot(y,t)\n            if rx == ry: return 0\n            else:\n                if sizes[t][rx] >= sizes[t][ry]:\n                    Parents[t][ry] = rx\n                    sizes[t][rx] = max(sizes[t][rx],sizes[t][ry])\n                else:\n                    Parents[t][rx] = ry\n                return 1\n            \n        def connect(thetype):\n            mytypes = [thetype] if thetype < 2 else [ 0, 1 ]\n            for node in range(n):\n                for neighbor in edges123[thetype][node]:\n                    if all(Uni(node,neighbor,t) for t in mytypes):\n                        for t in mytypes: selectedEdges[t] += 1\n                    else:\n                        self.res += 1\n            # for t in mytypes: \n            #     root = [FindRoot(i,t) for i in range(n)]\n            #     print(thetype,t, 'parents',Parents[t],root,selectedEdges,self.res)\n                \n        connect(2)\n        connect(0)\n        connect(1)\n        return self.res if all(selectedEdges[t]==n-1 for t in [0,1]) else -1\n", "class DisjointSet:\n    def __init__(self, n):\n        self.parent = {i: i for i in range(1, n+1)}\n        self.rank = {i: 1 for i in range(1, n+1)}\n        self.count = n\n        \n    def find(self, x):\n        if x != self.parent[x]:\n            self.parent[x] = self.find(self.parent[x])\n        return self.parent[x]\n        \n    def union(self, x, y):\n        root_x, root_y = self.find(x), self.find(y)\n        if root_x == root_y:\n            return False # x, y are already connected\n        self.count -= 1\n        if self.rank[root_x] > self.rank[root_y]:\n            self.parent[root_y] = root_x\n        elif self.rank[root_x] < self.rank[root_y]:\n            self.parent[root_x] = root_y\n        else:\n            self.parent[root_x] = root_y\n            self.rank[root_y] += 1\n        \n        return True\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        ds_a = DisjointSet(n)\n        ds_b = DisjointSet(n)\n        \n        out = 0\n        edges = sorted(edges, key=lambda e: e[0], reverse=True)\n        for t, u, v in edges:\n            if t == 3:\n                if not ds_a.union(u, v):\n                    out += 1\n                else:\n                    ds_b.union(u, v)\n            elif t == 2:\n                if not ds_b.union(u, v):\n                    out += 1\n            elif t == 1:\n                if not ds_a.union(u, v):\n                    out += 1\n        \n        if (ds_a.count > 1) or (ds_b.count > 1):\n            return -1\n        \n        return out\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        parent = {}\n        for i in range(1, n + 1):\n            parent[i] = i\n        \n        r = 0\n        \n        both = 0\n        for c, a, b in edges:\n            if c == 3:\n                if self.find(a, parent) == self.find(b, parent):\n                    r += 1\n                else:\n                    self.union(a, b, parent)\n                    both += 1\n        \n        alice = both\n        aliceP = parent.copy()\n        for c, a, b in edges:\n            if c == 1 or c == 3:\n                if self.find(a, aliceP) == self.find(b, aliceP):\n                    if c == 1:\n                        r += 1\n                else:\n                    self.union(a, b, aliceP)\n                    alice += 1\n        print(alice)\n        if alice < n - 1:\n            return -1\n        \n        bob = both\n        bobP = parent.copy()\n        for c, a, b in edges:\n            if c == 2 or c == 3:\n                if self.find(a, bobP) == self.find(b, bobP):\n                    if c == 2:\n                        r += 1\n                else:\n                    self.union(a, b, bobP)\n                    bob += 1\n        print(bob)\n        if bob < n - 1:\n            return -1\n        return r\n                \n                    \n    \n    def union(self, a, b, parent):\n        pa = self.find(a, parent)\n        pb = self.find(b, parent)\n        if pa == pb:\n            return\n        parent[pb] = pa\n        return\n    \n    def find(self, a, parent):\n        path = [a]\n        while a in parent and parent[a] != a:\n            a = parent[a]\n            path.append(a)\n        for p in path:\n            parent[p] = a\n        return a\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # \u6240\u6709\u8fb9\u6309\u7167\u7c7b\u578b\u6392\u5e8f\uff0c \u7c7b\u578b\u4e3a3\u7684\u8fb9\u6709\u6700\u9ad8\u4f18\u5148\u7ea7\n        edges.sort(key=lambda edge: edge[0], reverse=True)\n        \n        def build_graph(types):\n            removed = set()\n            neighbors = defaultdict(set)\n            graph = []\n            for (t, a, b) in edges: # edge: t, a->b\n                if t not in types: continue\n                if b in neighbors[a]:\n                    removed.add((t, a, b))\n                    continue\n                # print((t, a, b))\n                \n                neighbors[a].add(b)\n                neighbors[b].add(a)\n                graph.append((t, a, b))\n            # print('========')\n            return removed, graph\n        \n        def find(f, a):\n            if f[a] == 0: return a\n            if f[a] != a: f[a] = find(f, f[a])\n            return f[a]\n\n        def generate_tree(graph, removed):\n            f = defaultdict(int)\n            nodes = set()\n            for t, a, b in graph:\n                nodes.add(a)\n                nodes.add(b)\n                fa, fb = find(f, a), find(f, b)\n                if fa == fb: \n                    removed.add((t, a, b)) \n                else:\n                    f[fb] = fa\n        \n            return len(nodes) != n\n        \n        alice_removed, alice_graph = build_graph(set([1, 3]))\n        bob_removed, bob_graph = build_graph(set([2, 3]))\n        \n        if generate_tree(alice_graph, alice_removed): return -1\n        if generate_tree(bob_graph, bob_removed): return -1\n        \n        ans = len(alice_removed.union(bob_removed))\n        return ans", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # Union find\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n\n        res = e1 = e2 = 0\n\n        # Alice and Bob\n        root = list(range(n + 1))\n        for t, i, j in filter(lambda e: e[0] == 3, edges):\n            if uni(i, j):\n                e1 += 1\n                e2 += 1\n            else:\n                res += 1\n        root0 = root[:]\n\n        # only Alice\n        for t, i, j in edges:\n            if t == 1:\n                if uni(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n\n        # only Bob\n        root = root0\n        for t, i, j in edges:\n            if t == 2:\n                if uni(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if e1 == e2 == n - 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]], check_type = 1) -> int:\n        mem = {i: i for i in range(1, n + 1)}\n        def find(k):\n            if mem[k] != k:\n                mem[k] = find(mem[k])\n            return mem[k]\n        \n        def union(k1, k2):\n            f1, f2 = find(k1), find(k2)\n            if f1 != f2:\n                mem[f1] = f2\n        \n        res = 0\n        for t, e1, e2 in edges:\n            if t == 3:\n                f1, f2 = find(e1), find(e2)\n                if f1 == f2 and check_type == 1:\n                    res += 1\n                union(e1, e2)\n        \n        for t, e1, e2 in edges:\n            if t == check_type:\n                f1, f2 = find(e1), find(e2)\n                if f1 == f2:\n                    res += 1\n                else:\n                    union(f1, f2)\n\n        roots = set(map(find, list(range(1, n + 1))))\n        if len(roots) > 1:\n            return -1\n        \n        if check_type == 1:\n            res2 = self.maxNumEdgesToRemove(n, edges, check_type = 2)\n            if res2 == -1:\n                return -1\n            return res + res2\n        else:\n            return res\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges1,edges2,edges3 = [],[],[]\n        for t,u,v in edges:\n            if t==1:\n                E = edges1\n            if t==2:\n                E = edges2\n            if t==3:\n                E = edges3\n            E.append((u,v))\n        '''print(1, edges1)\n        print(2, edges2)\n        print(3, edges3)'''\n\n        cnt = 0\n        fathers = list(range(n+1))\n    \n        def find(fathers, v):\n            if fathers[v]!=v:\n                fathers[v]=find(fathers, fathers[v])\n            return fathers[v]\n        \n        def union(fathers, a, b):\n            rootA = find(fathers, a)\n            rootB = find(fathers, b)\n            if rootA != rootB:\n                fathers[rootB] = rootA\n                return True\n            return False\n        \n        # type 3\n        for u,v in edges3:\n            if not union(fathers, u, v):\n                cnt+=1\n        \n        #print(3, cnt)\n        #print(fathers)\n        def count(fathers, edges):\n            cnt = 0\n            for u,v in edges:\n                if not union(fathers, u, v):\n                    cnt+=1\n            for i,v in enumerate(fathers):\n                if i:\n                    find(fathers, i)\n            if len(set(fathers[1:]))>1:\n                return -1\n            return cnt\n        # Alice\n        a = count(fathers[:], edges1)\n        #print('alice', a)\n        if a==-1:\n            return -1\n        else:\n            cnt+=a\n        b = count(fathers[:], edges2)\n        #print('bob', b)\n        if b==-1:\n            return -1\n        else:\n            cnt+=b\n        return cnt\n    \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        alice = UnionFind(n)\n        bob = UnionFind(n)\n        \n        remove = 0\n        for t, u, v in edges:\n            if t == 3:\n                aliceMerge = alice.union(u, v)\n                bobMerge = bob.union(u, v)\n                if not aliceMerge and not bobMerge:\n                    remove += 1\n                \n        for t, u, v in edges:\n            if t != 3 and not alice.union(u, v) and not bob.union(u, v):\n                remove += 1\n                \n        if alice.num_of_components != 1 or bob.num_of_components != 1:\n            return -1\n        \n        return remove\n\nclass UnionFind:\n    \n    def __init__(self, n):\n        self.parent = {i+1: i+1 for i in range(n)}\n        self.size = {i+1: 1 for i in range(n)}\n        \n        self.num_of_components = n\n        \n    def find(self, p):\n        \n        root = p\n        while(root != self.parent[root]):\n            root = self.parent[root]\n            \n        node = p\n        while (node != self.parent[node]):\n            parent = self.parent[node]\n            self.parent[node] = root\n            node = parent\n            \n        return root\n    \n    def union(self, p, q):\n        rootP = self.find(p)\n        rootQ = self.find(q)\n        \n        if rootP == rootQ:\n            return False\n        \n        if self.size[rootP] > self.size[rootQ]:\n            self.size[rootP] += self.size[rootQ]\n            self.parent[rootQ] = rootP\n        else:\n            self.size[rootQ] += self.size[rootP]\n            self.parent[rootP] = rootQ\n        \n        self.num_of_components -= 1\n        return True", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges_alice = [e for e in edges if e[0] == 1 or e[0] == 3]\n        edges_bob = [e for e in edges if e[0] == 2 or e[0] == 3]\n        edges_both = [e for e in edges if e[0] == 3]\n        \n        nodes = [i for i in range(1, n + 1)]\n        \n        union_find = unionFind(nodes)\n        for edge in edges_alice:\n            union_find.union(edge[1], edge[2])\n        if union_find.count != 1:\n            return -1\n        num_removed_edge_alice = len(edges_alice) - (n - 1)\n        \n        union_find = unionFind(nodes)\n        for edge in edges_bob:\n            union_find.union(edge[1], edge[2])\n        if union_find.count != 1:\n            return -1\n        num_removed_edge_bob = len(edges_bob) - (n - 1)\n        \n        union_find = unionFind(nodes)\n        for edge in edges_both:\n            union_find.union(edge[1], edge[2])\n        num_removed_edge_both = len(edges_both) - (n - union_find.count)\n        \n        return num_removed_edge_alice + num_removed_edge_bob - num_removed_edge_both\n        \n\nclass unionFind:\n    def __init__(self, nodes):\n        self.father = {node: node for node in nodes}\n        self.count = len(nodes)\n        \n    def find(self, node):\n        if self.father[node] == node:\n            return node\n        self.father[node] = self.find(self.father[node])\n        return self.father[node]\n    \n    def union(self, node_a, node_b):\n        father_a = self.find(node_a)\n        father_b = self.find(node_b)\n        if father_a != father_b:\n            self.father[father_a] = father_b\n            self.count -= 1\n            \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # Union find\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n\n        res = e1 = e2 = 0\n\n        # Alice and Bob\n        root = list(range(n + 1))\n        for t, i, j in edges:\n            if t == 3:\n                if uni(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        root0 = root[:]\n\n        # only Alice\n        for t, i, j in edges:\n            if t == 1:\n                if uni(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n\n        # only Bob\n        root = root0\n        for t, i, j in edges:\n            if t == 2:\n                if uni(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if e1 == e2 == n - 1 else -1\n            \n            \n", "class DSU:\n    def __init__(self, n):\n        self.p = list(range(n))\n        self.r = [0] * n\n    def find(self, i): \n        if self.p[i] != i: \n            self.p[i] = self.find(self.p[i])\n        return self.p[i]\n    def union(self, i, j): \n        pi, pj = self.find(i), self.find(j)\n        if pi != pj:\n            if self.r[pi] >= self.r[pj]: \n                self.p[pj] = pi\n                self.r[pi] += (self.r[pi] == self.r[pj])\n            else: \n                self.p[pi] = pj\n            return True\n        return False\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        if not edges: return -1\n        uf1, n1, uf2, n2 = DSU(n), n, DSU(n), n\n        edges.sort(reverse=True, key = lambda x: x[0])\n        # t 1:alice, 2:bob, 3:both\n        ans = 0\n        for t, u, v in edges: \n            print((n1, n2))\n            if t == 3: \n                u1, u2 = uf1.find(u-1) == uf1.find(v-1), uf2.find(u-1) == uf2.find(v-1)\n                if u1 and u2 : \n                    ans += 1\n                else: \n                    if not u1: \n                        uf1.union(u-1, v-1)\n                        n1 -= 1\n                    if not u2: \n                        uf2.union(u-1, v-1)\n                        n2 -= 1\n                        \n            elif t == 1: \n                if uf1.find(u-1) != uf1.find(v-1): \n                    n1 -= uf1.union(u-1, v-1)\n                else:\n                    ans += 1\n            elif t == 2: \n                if uf2.find(u-1) != uf2.find(v-1): \n                    n2 -= uf2.union(u-1, v-1)\n                else:\n                    ans += 1\n                \n                \n#             if u1 and uf1.find(u-1) != uf1.find(v-1): \n#                 n1 -= uf1.union(u-1, v-1)\n#                 can_delete = False\n                    \n#             if u2 and uf2.find(u-1) != uf2.find(v-1):\n#                 n2 -= uf2.union(u-1, v-1)\n#                 can_delete = False\n                \n            # ans += can_delete\n        # print(uf1.p)\n        # print(uf2.p)\n        print((ans, n1, n2))\n        return ans if (n1 <= 1 and n2 <=1) else -1\n                \n                \n            \n            \n            \n            \n            \n", "from typing import List\nfrom collections import defaultdict\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        AlicePath = defaultdict(set)\n        BobPath = defaultdict(set)\n        CommonPath = defaultdict(set)\n        for type, u, v in edges:\n            if type == 1 or type == 3 :\n                AlicePath[u-1].add(v-1)\n                AlicePath[v-1].add(u-1)\n            if type == 2 or type == 3 :\n                BobPath[u-1].add(v-1)\n                BobPath[v-1].add(u-1)\n            if type == 3:\n                CommonPath[u-1].add(v-1)\n                CommonPath[v-1].add(u-1)\n\n        # \u8ba1\u7b97\u4e00\u7ec4\u8fb9\u53ef\u4ee5\u62c6\u5206\u4e3a\u51e0\u7ec4\u53ef\u8fde\u901a\u56fe\n        def count(m: defaultdict(set)):\n            visited = [False] * n\n            ret = 0 \n            \n            def dfs(i: int, isNewGraph:bool):\n                nonlocal ret\n                if visited[i]: return \n                visited[i] = True\n                # \u5916\u5c42\u904d\u5386\u65f6\uff0c\u4e3a\u65b0\u56fe\u3002\u5185\u90e8\u9012\u5f52\u65f6\u4e3a\u65e7\u7684\u8fde\u901a\u56fe\uff0c\u65e0\u9700\u589e\u52a0\u7edf\u8ba1\u6570\u76ee\n                if isNewGraph: ret += 1 \n                for endPoint in m[i]:\n                    dfs(endPoint, False)\n\n            for i in range(n):\n                dfs(i, True) # \u8fd9\u91ccTrue\u8868\u793a\u662f\u4ece\u5916\u5c42\u5f00\u59cbdfs\uff0c\u9047\u5230\u6ca1\u6709\u8bbf\u95ee\u8fc7\u7684\u8282\u70b9\u5c31\u9700\u8981\u5c06\u8ba1\u6570+1\n\n            return ret\n\n        if count(AlicePath) > 1 or count(BobPath)> 1 :\n            return -1\n\n        x = count(CommonPath)\n        return len(edges) - (n+x-2)\n\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        d_alice = {i: i for i in range(1, n + 1)}\n        d_bob = {i: i for i in range(1, n + 1)}\n        \n        def find(d, ind):\n            if d[ind] != ind:\n                d[ind] = find(d, d[ind])\n            return d[ind]\n        \n        def union(d, i1, i2):\n            d[find(d, i1)] = find(d, i2)\n        \n        edges.sort(reverse=True)\n        res = 0\n        \n        for typ, i, j in edges:\n            if typ == 3:\n                if find(d_alice, i) == find(d_alice, j) and find(d_bob, i) == find(d_bob, j):\n                    res += 1\n                    continue\n                union(d_alice, i, j)\n                union(d_bob, i, j)\n            elif typ == 2:\n                if find(d_alice, i) == find(d_alice, j):\n                    res += 1\n                    continue\n                union(d_alice, i, j)\n            else:\n                if find(d_bob, i) == find(d_bob, j):\n                    res += 1\n                    continue\n                union(d_bob, i, j)\n                \n        for i in range(2, n + 1):\n            if find(d_alice, i) != find(d_alice, i-1) or find(d_bob, i) != find(d_bob, i-1):\n                return -1\n        \n        return res\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n       \n        def findRoot(node,p):\n            if node not in p:\n                p[node] = node\n                return node\n            if p[node] != node:\n                p[node] = findRoot(p[node],p)\n            return p[node]\n        \n        def union(node1, node2, p):\n            if findRoot(node1,p) == findRoot(node2,p):\n                return 0\n            p[findRoot(node1,p)] = findRoot(node2,p)\n            return 1\n        \n        def checkUnited(p,n):\n            root = None\n            for node in p:\n                if root != None and findRoot(node, p) != root:\n                    return False\n                root = findRoot(node, p)\n            return len(p) == n\n        \n        cnt = 0\n        p1 = {}\n        p2 = {}\n        for edge in edges:\n            if edge[0] == 3:\n                cnt += union(edge[1], edge[2], p1)\n                union(edge[1], edge[2], p2)\n      \n        for edge in edges:\n            if edge[0] == 1:\n                cnt += union(edge[1], edge[2], p1)\n            if edge[0] == 2:\n                cnt += union(edge[1], edge[2], p2)\n       \n        \n        return len(edges) - cnt if checkUnited(p1,n) and checkUnited(p2,n) else -1", "class UnionFind:\n    def __init__(self, n):\n        self.p = {}\n        self.group = n\n        for i in range(1, n+1):\n            self.p[i] = i\n            \n\n    def unite(self, a, b):    \n        pa, pb = self.find(a), self.find(b)\n        if pa != pb:\n            self.p[pa] = pb\n            self.group -= 1\n            return True\n        return False\n\n    def find(self, a):\n        if self.p[a] != a:\n            self.p[a] = self.find(self.p[a]);\n        return self.p[a]\n\n    def united(self):\n        return self.group == 1\n    \nclass Solution:\n    # copied https://leetcode.com/problems/remove-max-number-of-edges-to-keep-graph-fully-traversable/discuss/831506/Textbook-Union-Find-Data-Structure-Code-with-Explanation-and-comments    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges = sorted(edges, reverse=True)\n        \n        edgesAdded = 0        \n        bob, alice = UnionFind(n), UnionFind(n)        \n        for edge in edges:\n            tp, one, two = edge[0], edge[1], edge[2]\n            if tp == 3:\n                bu = bob.unite(one, two)\n                au = alice.unite(one, two)\n                edgesAdded += 1 if bu or au else 0\n            elif tp == 2:\n                edgesAdded += bob.unite(one, two)\n            else:\n                edgesAdded += alice.unite(one, two)\n\n        return len(edges)-edgesAdded if bob.united() and alice.united() else -1\n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        total_edges = []\n        bob_edges = []\n        alice_edges = []\n        \n        \n        for (t, x, y) in edges:\n            x -= 1\n            y -= 1\n            if t == 1:\n                # bob[x].append(y)\n                # bob[y].append(x)\n                bob_edges.append((x, y))\n            elif t == 2:\n                # alice[x].append(y)\n                # alice[y].append(x)\n                alice_edges.append((x, y))\n            else:\n                # total[x].append(y)\n                # total[y].append(x)\n                total_edges.append((x, y))\n        \n        \n        def kruskal(colors, sets, edges):\n            used = 0\n            for (x, y) in edges:\n                if colors[x] == colors[y]:\n                    continue\n\n                used += 1\n                if len(sets[colors[x]]) < len(sets[colors[y]]):\n                    x, y = y, x\n\n                #add y to x\n                color_x = colors[x]\n                color_y = colors[y]\n\n                for node in sets[color_y]:\n                    colors[node] = color_x\n\n                sets[color_x].extend(sets[color_y])\n                \n            return used\n        \n        total_colors = list(range(n))\n        total_sets = [[i] for i in range(n)]\n        \n        used_total = kruskal(total_colors, total_sets, total_edges)\n        # print(\\\"New colors\\\", total_colors)\n        \n        \n        bob_colors = total_colors[::]\n        alice_colors = total_colors[::]\n        \n        bob_sets = [el[::] for el in total_sets]\n        alice_sets = [el[::] for el in total_sets]\n        \n        used_bob = kruskal(bob_colors, bob_sets, bob_edges)\n        used_alice = kruskal(alice_colors, alice_sets, alice_edges)\n        \n        # print(\\\"Shared uses\\\", used_total)\n        # print(\\\"Bob used\\\", used_bob)\n        # print(\\\"Alice used\\\", used_alice)\n        \n        if (used_total + used_bob != n - 1) or (used_total + used_alice != n - 1):\n            return -1\n        \n        return len(edges) - used_total - used_bob - used_alice\n", "class Solution:\n    def span(self,A,n,flag,par):\n        countt=0\n        countu=0\n        for i,(t,u,v) in enumerate(A):\n            if t==flag:\n                countt+=1\n                p1=u\n                while par[p1]>0:\n                    p1=par[p1]\n                p2=v\n                while par[p2]>0:\n                    p2=par[p2]\n                    \n                if p1==p2:\n                    continue\n                    \n                else:\n                    countu+=1\n                    if abs(par[p1])>abs(par[p2]):\n                        par[p1]-=par[p2]\n                        if u!=p1:\n                            par[u]=p1\n                        par[v]=p1\n                        par[p2]=p1\n                    else:\n                        par[p2]-=par[p1]\n                        if v!=p2:\n                            par[v]=p2\n                        par[u]=p2\n                        par[p1]=p2\n         \n        return countt,countu\n        \n    \n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges.sort(key=lambda x:x[0],reverse=True)\n        par=[-1]*(n+1)\n        ct,cu=self.span(edges,n,3,par)\n        if cu==n-1:\n            return len(edges)-cu\n        copy=par[:]\n        ct2,cu2=self.span(edges,n,2,par)\n        ct1,cu1=self.span(edges,n,1,copy)\n        if cu+cu2!=n-1 or cu+cu1!=n-1:\n            return -1\n        \n        return len(edges)-(cu+cu1+cu2)\n        \n        \n        \n        \n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        parent = [x for x in range(n+1)]\n        rank = [0]*(n+1)\n        \n        def find(x, parent):\n            if x!=parent[x]:\n                parent[x]=find(parent[x], parent)\n            return parent[x]\n        \n        def union(x,y, parent, rank):\n            xp, yp = find(x, parent), find(y, parent)\n            if xp==yp:\n                return False\n            if rank[xp]>rank[yp]:\n                parent[yp]=xp\n            elif rank[xp]<rank[yp]:\n                parent[xp]=yp\n            else:\n                parent[yp]=xp\n                rank[xp]+=1\n            return True\n        \n        type1 = [(u,v) for w,u,v in edges if w==1]\n        type2 = [(u,v) for w,u,v in edges if w==2]\n        type3 = [(u,v) for w,u,v in edges if w==3]\n        \n            \n        ans = 0\n        for u,v in type3:\n            if not union(u,v, parent, rank):\n                ans+=1\n                \n        p1 = parent.copy()\n        r1 = rank.copy()\n        \n        for u,v in type1:\n            if not union(u,v,p1,r1):\n                ans+=1\n                \n        for u,v in type2:\n            if not union(u,v,parent,rank):\n                ans+=1\n                \n        arr1 = [find(p,p1) for p in p1[1:]]\n        arr2 = [find(p,parent) for p in parent[1:]]\n        if (arr1[1:] != arr1[:-1]) or (arr2[1:] != arr2[:-1]):\n            return -1\n        return ans", "class UnionNode:\n    def __init__(self):\n        self.parent = None\n        self.order = 0\n\n\nclass UnionSet:\n    def __init__(self, N):\n        self.els = [UnionNode() for i in range(N)]\n        self.edge_count = 0\n\n\n    def find(self, i):\n        node = self.els[i]\n\n        if node.parent is None:\n            return i\n\n        node.parent = self.find(node.parent)\n        return node.parent\n\n\n    def union(self, i, j):\n        i = self.find(i)\n        j = self.find(j)\n\n        if i == j:\n            return\n\n        self.edge_count += 1\n\n        i_node = self.els[i]\n        j_node = self.els[j]\n\n        if i_node.order < j_node.order:\n            i_node, j_node = j_node, i_node\n            i, j = j, i\n\n        j_node.parent = i\n        i_node.order += (i_node.order == j_node.order)\n\n\n    def connected(self):\n        return len(self.els) == self.edge_count + 1\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        alice_union  = UnionSet(n)\n        bob_union    = UnionSet(n)\n        shared_union = UnionSet(n)\n\n        for (type_, u, v) in edges:\n            if type_ in (1, 3):\n                alice_union.union(u - 1, v - 1)\n\n            if type_ in (2, 3):\n                bob_union.union(u - 1, v - 1)\n\n            if type_ == 3:\n                shared_union.union(u - 1, v - 1)\n\n        if not alice_union.connected() or not bob_union.connected():\n            return -1\n\n        missing_edges = n - 1 - shared_union.edge_count\n        needed_edges = shared_union.edge_count + 2*missing_edges\n\n        return len(edges) - needed_edges", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        class UnionFind:\n            def __init__(self):\n                self.parent = {}\n                self.e = 0\n            def find(self, a):\n                if a not in self.parent:\n                    self.parent[a] = a\n                p = a\n                while p != self.parent[p]:\n                    p = self.parent[p]\n                while a != p:\n                    tmp = a\n                    self.parent[a] = p\n                    a = self.parent[tmp]\n                return p\n            def union(self, a, b):\n                pa, pb = self.find(a), self.find(b)\n                if pa != pb:\n                    self.parent[pa] = pb\n                    self.e += 1\n                    return 0\n                return 1\n        \n        ufa, ufb = UnionFind(), UnionFind()\n        ans = 0\n        for t, u, v in edges:\n            if t == 3:\n                ans += ufa.union(u, v)\n                ufb.union(u, v)\n                \n        for t, u, v in edges:\n            if t == 1:\n                ans += ufa.union(u, v)\n            if t == 2:\n                ans += ufb.union(u, v)\n                \n        return ans if (ufa.e == n-1 and ufb.e == n-1) else -1\n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        p1 = list(range(n + 1))\n        p2 = list(range(n + 1))\n\n        def find(parents, node):\n            if parents[node] != node:\n                parents[node] = find(parents, parents[node])\n            return parents[node]\n        \n        def union(parents, a, b):\n            ra = find(parents, a)\n            rb = find(parents, b)\n            if ra != rb:\n                parents[ra] = rb\n                return True\n            return False\n        \n        def count(parents, n):\n            res = 0\n            root = find(parents, 1)\n            for i in range(n + 1):\n                if find(parents, i) == root:\n                    res += 1\n            return res\n        \n        res = 0\n        edges.sort(reverse = True)\n        for t, u, v in edges:\n            if t == 1 and not union(p1, u, v):\n                res += 1\n            if t == 2 and not union(p2, u, v):\n                res += 1\n            if t == 3:\n                del1 = not union(p1, u, v)\n                del2 = not union(p2, u, v)\n                if (del1 and del2):\n                    res += 1\n        if count(p1, n) == n and count(p2, n) == n:\n            return res\n        return -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(i, parents):\n            if parents[i] != i:\n                parents[i] = find(parents[i], parents)\n            return parents[i]\n            \n        def union(i, j, parents, groups):\n            p_i = find(i, parents)\n            p_j = find(j, parents)\n            if p_i != p_j:\n                groups -= 1\n                if p_i > p_j:\n                    parents[p_j] = p_i\n                else:\n                    parents[p_i] = p_j\n            return groups\n        \n        alice = []\n        bob = []\n        res = 0\n        \n        parents = list(range(n+1))\n        groups = n  \n        \n        for t, a, b in edges:\n            if t == 1:\n                alice.append((a, b))\n            elif t == 2:\n                bob.append((a, b))\n            else:\n                if find(a, parents) == find(b, parents):\n                    res += 1\n                else:\n                    groups = union(a, b, parents, groups)\n                    \n        if groups == 1:\n            return res + len(alice) + len(bob)\n        \n        ga = groups\n        gb = groups\n        pa = parents[:]\n        pb = parents[:]\n        \n        while alice:\n            i, j = alice.pop()\n            if find(i, pa) == find(j, pa):\n                res += 1\n            else:\n                ga = union(i, j, pa, ga)\n            if ga == 1:\n                res += len(alice)\n                break\n                \n        if ga != 1:\n            return -1\n        \n        while bob:\n            i, j = bob.pop()\n            if find(i, pb) == find(j, pb):\n                res += 1\n            else:\n                gb = union(i, j, pb, gb)\n            if gb == 1:\n                res += len(bob)\n                break\n                \n        if gb != 1:\n            return -1\n        \n        return res\n        \n            \n", "class UnionSet:\n    def __init__(self, n):\n        self.parent = {i:i for i in range(1, n+1)}\n        # self.level = {i:0 for i in range(1, n+1)}\n    \n    def union(self, i, j):\n        # if self.level[i] < self.level[j]:\n        self.parent[self.get(i)] = self.get(j)\n        \n    def get(self, i):\n        if self.parent[i] == i:\n            return i\n        else:\n            self.parent[i] = self.get(self.parent[i])\n            return self.parent[i]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n                    \n        mapping = {k: [] for k in range(1, 4)}\n        alice = UnionSet(n)\n        bob = UnionSet(n)\n        for t, u, v in edges:\n            mapping[t].append((v, u))\n\n        \n        for u, v in mapping[3]:\n            alice.union(u, v)\n        bob.parent = alice.parent.copy()\n        \n        seen = set()\n        for i in range(1, n+1):\n            seen.add(alice.get(i))\n        \n        after3 = len(seen)\n        \n        for u,v in mapping[1]:\n            alice.union(u, v)\n        for u, v in mapping[2]:\n            bob.union(u, v)\n        \n        seen = set()\n        for i in range(1, n+1):\n            seen.add(alice.get(i))\n        if len(seen) != 1:\n            return -1\n\n        seen = set()\n        for i in range(1, n+1):\n            seen.add(bob.get(i))\n        if len(seen) != 1:\n            return -1\n    \n    \n        delete3 = len(mapping[3]) - (n - after3)\n        use3 = len(mapping[3]) - delete3\n        return len(mapping[1]) + len(mapping[2]) + use3 * 2 - 2 * (n - 1) + delete3", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        class UnionFind:\n            def __init__(self):\n                self.parent = {}\n                self.e = 0\n            def find(self, a):\n                if a not in self.parent:\n                    self.parent[a] = a\n                p = a\n                while p != self.parent[p]:\n                    p = self.parent[p]\n                while a != p:\n                    tmp = a\n                    self.parent[a] = p\n                    a = self.parent[tmp]\n                return p\n            def union(self, a, b):\n                pa, pb = self.find(a), self.find(b)\n                if pa != pb:\n                    self.parent[pa] = pb\n                    self.e += 1\n                    return 1\n                return 0\n        \n        ufa, ufb = UnionFind(), UnionFind()\n        ans = 0\n        for t, u, v in edges:\n            if t == 3:\n                ans += ufa.union(u, v)\n                ufb.union(u, v)\n                \n        for t, u, v in edges:\n            if t == 1:\n                ans += ufa.union(u, v)\n            if t == 2:\n                ans += ufb.union(u, v)\n                \n        return (len(edges) - ans) if (ufa.e == n-1 and ufb.e == n-1) else -1\n        \n", "class UnionFind:\n    def __init__(self, size):\n        self.father = [i for i in range(size + 1)]\n\n    def find(self, x):\n        if self.father[x] == x:\n            return x \n        self.father[x] = self.find(self.father[x])\n        return self.father[x]\n\n    def connect(self, a, b):\n        root_a, root_b = self.find(a), self.find(b)\n        if root_a != root_b:\n            self.father[root_a] = root_b\n\nclass Solution:\n    def mst(self, n, edges, t):\n        g = sorted([e for e in edges if e[0] in (t,3)], key = lambda x : -x[0])\n        UF, u = UnionFind(n), set()\n        for t, a, b in g:\n            if UF.find(a) != UF.find(b):\n                UF.connect(a,b)\n                u.add((t,a,b))\n        return len({UF.find(i) for i in range(1, n + 1)}) == 1, u \n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        traversed1, mst1 = self.mst(n, edges, 1)\n        traversed2, mst2 = self.mst(n, edges, 2)\n        if (not traversed1) or (not traversed2):\n            return -1 \n        return len(edges) - len(mst1 | mst2)\n", "from typing import Tuple\nclass Solution:\n    def __init__(self):\n      self.roots = {}\n      self.ranks = {}\n      self.groups = 0\n    \n    def find(self, node_info: Tuple[int, int]):\n      self.roots.setdefault(node_info, node_info)\n      self.ranks.setdefault(node_info, 1)\n      if self.roots[node_info] != node_info:\n        self.roots[node_info] = self.find(self.roots[node_info])\n      return self.roots[node_info]\n    \n    def union(self, node_info1, node_info2) -> bool:  # returns if the edge can be removed\n      root1, root2 = self.find(node_info1), self.find(node_info2)\n      if root1 != root2:\n        self.groups -= 1\n        if self.ranks[root2] < self.ranks[root1]:\n          self.roots[root2] = root1\n        elif self.ranks[root1] < self.ranks[root2]:\n          self.roots[root1] = root2\n        else:\n          self.roots[root2] = root1\n          self.ranks[root1] += 1\n        return False  # we can't remove this edge because it's used\n      else:\n        return True  # we can remove this edge because there already is a path for these 2 nodes.\n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n      \\\"\\\"\\\"\n      Union find for alice and bob separately. But the trick here is that we must go over type 3 first, because\n      once we have type 3, then for the same node pair, we don't ever need type1 or type 2. But if we see types \n      in such order: 1, 3, 2. We can remove type 1, but we can't remove type2, because at that point type 1 is\n      already removed.\n      Time:\n      So we need to sort the edges by type first: O(eloge), then iterate over the edges O(e)\n      In fact, we can remove the sort if we loop over the edges twice, the first time we only do type 3, and\n      then the second type we do the other 2 types. This removes the sorting complexity.\n      \\\"\\\"\\\"\n      # with sorting:\n#       edges.sort(key=lambda edge: -edge[0])  \n#       removes = 0\n#       self.groups = n * 2\n#       for tp, n1, n2 in edges:\n#         can_remove = False\n#         if tp == 1:\n#           can_remove = self.union((1, n1), (1, n2))\n#         elif tp == 2:\n#           can_remove = self.union((2, n1), (2, n2))\n#         else:\n#           can_remove1, can_remove2 = self.union((1, n1), (1, n2)), self.union((2, n1), (2, n2))\n#           can_remove = can_remove1 and can_remove2\n#         removes += (1 if can_remove else 0)  \n      \n#       # If in the end both alice and alice have a single group, then return removed count\n#       return removes if self.groups == 2 else -1 \n      \n      # without sorting:\n      removes = 0\n      self.groups = n * 2\n      for tp, n1, n2 in edges:  # first iteration\n        if tp == 3:\n          can_remove1, can_remove2 = self.union((1, n1), (1, n2)), self.union((2, n1), (2, n2))\n          removes += (1 if can_remove1 and can_remove2 else 0)  \n      \n      for tp, n1, n2 in edges:\n        can_remove = False\n        if tp == 1:\n          can_remove = self.union((1, n1), (1, n2))\n        elif tp == 2:\n          can_remove = self.union((2, n1), (2, n2))\n        removes += (1 if can_remove else 0)\n      \n      return removes if self.groups == 2 else -1 ", "# \u65e0\u5411\u56fe\u7684\u751f\u6210\u6811\n# \u751f\u6210\u6811->\u6709G\u7684\u5168\u90e8v\uff0c\u4f46\u8fb9\u6570\u6700\u5c11\u7684\u8fde\u901a\u5b50\u56fe; \u5e26\u6743->\u53ef\u4ee5\u627emst\n# 3->\u9047\u73af\u5220 \u5f97\u8fb9\u6570\u76ee \n# 3\uff0c1->\u9047\u73af\u5220 \u5f97\u8fb9\u6570\u76ee ?=n-1\n# 3\uff0c2->\u9047\u73af\u5220 \u5f97\u8fb9\u6570\u76ee ?=n-1\nfrom collections import defaultdict\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def get_circle(comb):\n            G = defaultdict(list)\n            for t,u,v in edges:\n                if t in comb:\n                    G[u].append(v)\n                    G[v].append(u)\n            cnt = 0\n            visited = set()\n            for u in list(G.keys()):\n                if u in visited:\n                    continue\n                visited.add(u)\n                stack = [u]\n                while stack:\n                    cur = stack.pop()\n                    for nei in G[cur]:\n                        if nei not in visited:\n                            visited.add(nei)\n                            cnt+=1\n                            stack.append(nei)\n            return cnt \n        \n        type3 = get_circle((3,))\n        if type3==n-1:\n            return len(edges)-n+1   \n        type2 = get_circle((2,3))       \n        type1 = get_circle((1,3))\n        if type2==type1==n-1:\n            return len(edges)- (2*n-2-type3)\n        else: \n            return -1\n        \n        \n                    \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(p, i):\n            if p[i] != i:\n                p[i] = find(p, p[i])\n            return p[i]\n        \n        def union(p, i, j):\n            p[find(p, i)] = find(p, j)\n        \n        parents = list(range(n))\n        ans = 0\n        \n        edges = [(type, u - 1, v - 1) for type, u, v in edges]\n        \n        for type, u, v in edges:\n            if type == 3:\n                if find(parents, u) == find(parents, v):\n                    ans += 1\n                union(parents, u, v)\n        \n        alice = list(parents)\n        bob = list(parents)\n        \n        for type, u, v in edges:\n            if type == 1:\n                if find(alice, u) == find(alice, v):\n                    ans += 1\n                union(alice, u, v)\n            if type == 2:\n                if find(bob, u) == find(bob, v):\n                    ans += 1\n                union(bob, u, v)\n        \n        return ans if len({find(alice, i) for i in range(n)}) == 1 and len({find(bob, i) for i in range(n)}) == 1 else -1\n        \n                    \n", "class dsu:\n    def __init__(self, n):\n        self.n = n\n        self.par = [i for i in range(n+1)]\n    def find(self,x):\n        if self.par[x]==x:return x\n        self.par[x] = self.find(self.par[x])\n        return self.par[x]\n    def union(self,x,y):\n        x = self.find(x)\n        y = self.find(y)\n        if x==y: return 1\n        self.par[x] = y\n        return 0\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        gr1 = collections.defaultdict(list)\n        gr2 = collections.defaultdict(list)\n        edges1 = []\n        edges2 = []\n        mp = {1:1, 2:1, 3:-1}\n        for typ,x,y in edges:\n            if typ==3 or typ==1:\n                edges1.append([mp[typ], x,y])\n            if typ==3 or typ==2:\n                edges2.append([mp[typ], x,y])\n        edges1.sort()\n        edges2.sort()\n        dsu1 = dsu(n)\n        dsu2 = dsu(n)\n        oth1=oth2=0\n        res =0\n        for typ,x,y in edges1:\n            if dsu1.union(x,y):\n                if typ!=-1: res+=1\n                else: oth1+=1\n        for typ,x,y in edges2:\n            if dsu2.union(x,y):\n                if typ!=-1: res+=1\n                else: oth2+=1\n        count = 0\n        for i in range(1,n+1):\n            if i==dsu1.par[i]:count+=1\n            if i==dsu2.par[i]:count+=1\n        if count>2:return -1\n        return res+min(oth1,oth2)\n            \n        \n", "class UnionFind:\n    def __init__(self,n):\n        self.parent = list(range(n))\n        self.size = 1\n    \n    def find(self,x):\n        if x!=self.parent[x]:\n            self.parent[x] = self.find(self.parent[x])\n        return self.parent[x]\n    \n    def union(self,x,y):\n        parentx,parenty = self.find(x),self.find(y)\n        if parentx==parenty:\n            return False\n        self.parent[parentx] = parenty\n        self.size+=1\n        return True\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1,uf2 = UnionFind(n),UnionFind(n)\n        res = 0\n        \n        for t,u,v in edges:\n            if t!=3:\n                continue\n            if (not uf1.union(u-1,v-1)) or not (uf2.union(u-1,v-1)):\n                res += 1\n        \n        for t,u,v in edges:\n            if t==1 and not uf1.union(u-1,v-1):\n                res+=1\n            elif t==2 and not uf2.union(u-1,v-1):\n                res+=1\n        \n        return res if (uf1.size==n and uf2.size==n) else -1", "class UF:\n    def __init__(self, n):\n        self.parent = [i for i in range(n)]\n        self.size = [1] * n\n        \n    def isConnected(self, a, b):\n        return self.find(a) == self.find(b)\n    \n    def find(self, a):\n        while self.parent[a] != a:            \n            # path compression\n            self.parent[a] = self.parent[self.parent[a]]\n            a = self.parent[a]\n        return a\n    \n    def union(self, a, b):\n        p1 = self.find(a)\n        p2 = self.find(b)\n        if self.isConnected(p1, p2):\n            return\n        \n        # optimization - try to make the new tree balanced\n        if self.size[p1] < self.size[p2]:\n            self.parent[p1] = p2\n            self.size[p2] += self.size[p1]\n        else:\n            self.parent[p2] = p1\n            self.size[p1] += self.size[p2]\n                   \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf_1 = UF(n)\n        uf_2 = UF(n)\n        res = 0\n        edges = sorted(edges, key = lambda edges: edges[0], reverse = True) \n        \n        for t, a, b in edges:\n            if t == 3:\n                if uf_1.isConnected(a-1, b-1) and uf_2.isConnected(a-1, b-1):\n                    res += 1\n                else:\n                    uf_1.union(a-1, b-1)\n                    uf_2.union(a-1, b-1)\n            elif t == 1:\n                if uf_1.isConnected(a-1, b-1):\n                    res += 1\n                else:\n                    uf_1.union(a-1, b-1)\n            else:\n                if uf_2.isConnected(a-1, b-1):\n                    res += 1\n                else:\n                    uf_2.union(a-1, b-1)\n                    \n        return res if self.isValid(uf_1, n) and self.isValid(uf_2, n) else -1\n    \n    def isValid(self, uf, n):\n        for i in range(0, n-1):\n            if not uf.isConnected(i, i+1):\n                return False\n        return True\n    \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        alice = DSU()\n        bob = DSU()\n        res = 0\n        for t, u, v in edges:\n            if t == 3:\n                if alice.find(u) == alice.find(v):\n                    res += 1\n                else:\n                    alice.union(u, v)\n                    bob.union(u, v)\n                    \n        for t, u, v in edges:\n            if t == 1:\n                if alice.find(u) == alice.find(v):\n                    res += 1\n                else:\n                    alice.union(u, v)\n            if t == 2:\n                if bob.find(u) == bob.find(v):\n                    res += 1       \n                else:\n                    bob.union(u, v)\n                    \n        if max(bob.count.values()) != n or max(alice.count.values()) != n:\n            return -1\n        \n        return res\n        \nclass DSU:\n    def __init__(self):\n        self.father = {}\n        self.count = defaultdict(lambda:1)\n    \n    def find(self, a):\n        self.father.setdefault(a, a)\n        if a != self.father[a]:\n            self.father[a] = self.find(self.father[a])\n        return self.father[a]\n    \n    def union(self, a, b):\n        _a = self.find(a)\n        _b = self.find(b)\n        if _a != _b:\n            self.father[_a] = self.father[_b]\n            self.count[_b] += self.count[_a]", "#number of unneccessary type 3 + redudant type 1 + redudant type 2\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        res = 0\n        \n        #find all type 3\n        type3 = self.getEdges(edges, 3)\n        #find how many redudant edges for only the give connected vertices\n        redudant_edges = self.findRedudantEdges(type3, n + 1)\n        res += len(redudant_edges)\n        \n        type3 = list(set(type3) - set(redudant_edges))\n        \n        type1 = type3 + self.getEdges(edges, 1)\n        redudant_edges = self.findRedudantEdges(type1, n + 1)\n        #test if Bob and Alice can reach all edges\n        if len(type1) - len(redudant_edges) != n - 1:\n            return -1\n        \n        res += len(redudant_edges)\n        \n        type2 = type3 + self.getEdges(edges, 2)\n        redudant_edges = self.findRedudantEdges(type2, n + 1)\n        if len(type2) - len(redudant_edges) != n - 1:\n            return -1\n        \n        res += len(redudant_edges)\n        \n        return res \n    \n    #use Union-Find\n    def findRedudantEdges(self, edges, n):\n        res = []\n        parents = [i for i in range(n)]\n        \n        def findParent(a):\n            if a != parents[a]:\n                parents[a] = findParent(parents[a])\n            \n            return parents[a]\n        \n        for u, v in edges:\n            pu = findParent(u)\n            pv = findParent(v)\n            if pu == pv: \n                res.append((u, v))\n            else:\n                parents[pu] = pv\n                \n        return res\n    \n        \n    def getEdges(self, edges, type_num):\n        return [(u, v) for t, u, v in edges if t == type_num]\n        \n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        n_a={}\n        n_b={}\n        for e in edges:\n            if e[0]==1 or e[0]==3:\n                if e[1] not in n_a:\n                    n_a[e[1]]=[]\n                n_a[e[1]].append(e[2])\n                if e[2] not in n_a:\n                    n_a[e[2]]=[]\n                n_a[e[2]].append(e[1])\n            if e[0]==2 or e[0]==3:\n                if e[1] not in n_b:\n                    n_b[e[1]]=[]\n                n_b[e[1]].append(e[2])\n                if e[2] not in n_b:\n                    n_b[e[2]]=[]\n                n_b[e[2]].append(e[1])\n                \n        visited=set()\n        l=list(n_b.keys())\n        start=l[0]\n        visited.add(start)\n        q=[start]\n        while q:\n            actual=q[0]\n            del q[0]\n            if actual in n_a:\n                for nb in n_a[actual]:\n                    if nb not in visited:\n                        visited.add(nb)\n                        q.append(nb)\n        if len(visited)!=n:\n            return -1\n        \n        visited=set()\n        l=list(n_b.keys())\n        start=l[0]\n        visited.add(start)\n        q=[start]\n        while q:\n            actual=q[0]\n            del q[0]\n            if actual in n_b:\n                for nb in n_b[actual]:\n                    if nb not in visited:\n                        visited.add(nb)\n                        q.append(nb)\n        if len(visited)!=n:\n            return -1        \n\n        \n        \n        \n        \n        \n        \n        parent_a={}\n        parent_b={}\n        for i in range(1,n+1):\n            parent_a[i]=i\n            parent_b[i]=i\n            \n        def find_a(x):\n            if parent_a[x]!=x:\n                parent_a[x]=find_a(parent_a[x])\n            return parent_a[x]\n        def find_b(x):\n            if parent_b[x]!=x:\n                parent_b[x]=find_b(parent_b[x])\n            return parent_b[x]\n        def union_a(x,y):\n            x=find_a(x)\n            y=find_a(y)\n            if x!=y:\n                parent_a[x]=y\n                return 0\n            else:\n                return 1\n        def union_b(x,y):\n            x=find_b(x)\n            y=find_b(y)\n            if x!=y:\n                parent_b[x]=y\n                return 0\n            else:\n                return 1\n        count=0\n        for e in edges:\n            if e[0]==3:\n                u1=union_a(e[1],e[2])\n                u2=union_b(e[1],e[2])\n                if u1==u2 and u1==1:\n                    count+=1\n \n        for e in edges:\n            if e[0]==1:    \n                u1=union_a(e[1],e[2])\n                if u1==1:\n                    count+=1\n            if e[0]==2:    \n                u2=union_b(e[1],e[2])\n                if u2==1:\n                    count+=1\n        \n        return count\n                    \n        \n", "\nclass dsu:\n    def __init__(self,n):\n        self.parent =[-1 for i in range(n)]\n        self.size = [0 for i in range(n)]\n\n    def make_set(self,node):\n        self.parent[node] = node\n\n    def find(self,a):\n        if self.parent[a] == a:\n            return self.parent[a]\n        else:\n            self.parent[a] = self.find(self.parent[a])\n            return self.parent[a]\n\n    def union(self,a,b):\n        a = self.find(a)\n        b = self.find(b)\n        if a!=b:\n            if self.size[a] < self.size[b]:\n                temp = a\n                a = b\n                b = temp\n            self.parent[b] =self.parent[a]\n            self.size[a] = self.size[a] + self.size[b]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        ans = 0\n        treeA = dsu(n+1)\n        treeB = dsu(n+1)\n        for i in range(n+1):\n            treeA.make_set(i)\n            treeB.make_set(i)\n        edges.sort(reverse = True)\n        # print(edges)\n        m = len(edges)\n        i = 0\n        while(i<m and edges[i][0]==3):\n            if treeA.find(edges[i][1]) == treeA.find(edges[i][2]) :\n                ans= ans + 1\n                i= i +1\n                continue\n            \n            treeA.union(edges[i][1],edges[i][2])\n            treeB.union(edges[i][1],edges[i][2])\n            i= i +1\n        # print(treeB.parent,\\\"B\\\")\n        # print(treeA.parent,\\\"A\\\")\n        while(i<m and edges[i][0]==2):\n            if treeA.find(edges[i][1]) == treeA.find(edges[i][2]) :\n                ans= ans + 1\n                i= i +1\n                continue\n            treeA.union(edges[i][1],edges[i][2])\n            # print(treeA.parent,\\\"A\\\")\n            i= i +1\n            \n        while(i<m and edges[i][0]==1):\n            if treeB.find(edges[i][1]) == treeB.find(edges[i][2]) :\n                ans= ans + 1\n                print(\\\"here\\\")\n                i= i +1\n                continue\n            treeB.union(edges[i][1],edges[i][2])\n            # print(treeB.parent,\\\"B\\\",ans,edges[i])\n            i= i +1\n        def check(tree):\n            curr= tree.find(1)\n            for i in range(1,n+1):\n                if tree.find(i) != curr:\n                    return 0\n            return 1\n        # print(ans)\n        if check(treeA) == 0 or check(treeB) == 0:\n            return -1\n        return ans\n                \n        \n        ", "from typing import Tuple\nclass Solution:\n    def __init__(self):\n      self.roots = {}\n      self.ranks = {}\n      self.groups = 0\n    \n    def find(self, node_info: Tuple[int, int]):\n      self.roots.setdefault(node_info, node_info)\n      self.ranks.setdefault(node_info, 1)\n      if self.roots[node_info] != node_info:\n        self.roots[node_info] = self.find(self.roots[node_info])\n      return self.roots[node_info]\n    \n    def union(self, node_info1, node_info2) -> bool:  # returns if the edge can be removed\n      root1, root2 = self.find(node_info1), self.find(node_info2)\n      if root1 != root2:\n        self.groups -= 1\n        if self.ranks[root2] < self.ranks[root1]:\n          self.roots[root2] = root1\n        elif self.ranks[root1] < self.ranks[root2]:\n          self.roots[root1] = root2\n        else:\n          self.roots[root2] = root1\n          self.ranks[root1] += 1\n        return False  # we can't remove this edge because it's used\n      else:\n        return True  # we can remove this edge because there already is a path for these 2 nodes.\n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n      \\\"\\\"\\\"\n      Union find for alice and bob separately.\n      \\\"\\\"\\\"\n      edges.sort(key=lambda edge: -edge[0])\n      removes = 0\n      self.groups = n * 2\n      for tp, n1, n2 in edges:\n        can_remove = False\n        if tp == 1:\n          can_remove = self.union((1, n1), (1, n2))\n        elif tp == 2:\n          can_remove = self.union((2, n1), (2, n2))\n        else:\n          can_remove1, can_remove2 = self.union((1, n1), (1, n2)), self.union((2, n1), (2, n2))\n          can_remove = can_remove1 and can_remove2\n        removes += (1 if can_remove else 0)  \n      \n      # If in the end both alice and alice have a single group, then return removed count\n      return removes if self.groups == 2 else -1 ", "class UnionFind:\n    def __init__(self, n):\n        self.par = list(range(n+1))\n        self.sz = list(range(n+1))\n    \n    def find(self, i):\n        while i != self.par[i]:\n            i = self.par[i]\n        return i\n    \n    def union(self, x, y):\n        x,y = self.find(x), self.find(y)\n        if x == y: return 0\n        if self.sz[x] < self.sz[y]:\n            x, y = y, x\n        self.par[y] = x\n        self.sz[x] += self.sz[y]\n        return 1\n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges = sorted(edges, key=lambda x: -x[0])\n        uf = UnionFind(n)\n        res = e1 = e2 = 0\n        for t, u, v in edges:\n            if t == 3:\n                if uf.union(u,v):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        p0 = uf.par[:]\n        for t, u, v in edges:\n            if t ==2:\n                if uf.union(u,v):\n                    e2 += 1\n                else:\n                    res += 1\n        uf.par = p0\n        for t, u, v in edges:\n            if t == 1:\n                if uf.union(u,v):\n                    e1 += 1\n                else:\n                    res += 1\n        return res if e1 == e2 == n-1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(p,x):\n            if p[x] != x:\n                p[x] = find(p,p[x])\n            return p[x]  \n        def merge(p,x, y):\n            x = p[x]\n            y = p[y]\n            if x<y:\n                p[y] = x\n            else:\n                p[x] = y\n        parent = [x for x in range(n+1)]\n        path = 0\n        for e in edges:\n            if e[0] == 3 and find(parent, e[1]) != find(parent, e[2]):\n                merge(parent,e[1],e[2])\n                path += 1\n        a, b = list(parent), list(parent)\n        patha, pathb = 0, 0\n        for e in edges:\n            if e[0] == 1 and find(a, e[1]) != find(a, e[2]):\n                merge(a,e[1],e[2])\n                patha += 1\n            if e[0] == 2 and find(b, e[1]) != find(b, e[2]):\n                merge(b,e[1],e[2])\n                pathb += 1   \n        if patha + path != n-1 or pathb + path != n - 1: return -1 \n        return len(edges) - (path + patha + pathb)\n                       \n\n", "'''\nn is the number of nodes\nif 1 < value of nodes <= n\nKrustal O(ElogE)\n\nrank[node]: the longth depth of node's children\n'''\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n, edges):\n        # Union find\n        def find(node):\n            if node != parent[node]:\n                parent[node] = find(parent[node])\n            return parent[node]\n\n        def uni(node1, node2):\n            parent1, parent2 = find(node1), find(node2)\n            if parent1 == parent2: return 0\n            if rank[parent1] > rank[parent2]:\n                parent[parent2] = parent1\n            elif rank[parent1] == rank[parent2]:\n                parent[parent2] = parent1\n                rank[parent1] += 1\n            else:\n                parent[parent1] = parent2 \n            \n            return 1\n\n        res = union_times_A = union_times_B = 0\n\n        # Alice and Bob\n        parent = [node for node in range(n + 1)]\n        rank = [0 for node in range(n + 1)]\n        \n        for t, node1, node2 in edges:\n            if t == 3:\n                if uni(node1, node2):\n                    union_times_A += 1\n                    union_times_B += 1\n                else:\n                    res += 1\n        parent0 = parent[:]  # Alice union will change the parent array, keep origin for Bob\n\n        # only Alice\n        for t, node1, node2 in edges:\n            if t == 1:\n                if uni(node1, node2):\n                    union_times_A += 1\n                else:\n                    res += 1\n\n        # only Bob\n        parent = parent0\n        for t, node1, node2 in edges:\n            if t == 2:\n                if uni(node1, node2):\n                    union_times_B += 1\n                else:\n                    res += 1\n# only if Alice and Bob both union n-1 times, the graph is connected for both of them\n        return res if union_times_A == union_times_B == n - 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def root(i):\n            while par[i] != i:\n                par[i] = par[par[i]]\n                i = par[i]\n            return i\n        \n        def connected(i, j):\n            x = root(i)\n            y = root(j)\n            return x == y\n        \n        def union(i, j):\n            x = root(i)\n            y = root(j)\n            if x == y:\n                return\n            if sz[x] <= sz[y]:\n                par[x] =  y\n                sz[y] += sz[x]\n            else:\n                par[y] = x\n                sz[x] += sz[y]\n                \n        par = {}\n        sz  = {}\n        st = set()\n        edges.sort(key = lambda x : 0 - x[0])\n        count = 0\n        for e in edges:\n            t = e[0]\n            x = e[1]\n            y = e[2]\n            st.add(x)\n            st.add(y)\n            \n            xa = str(e[1]) + \\\"a\\\"\n            xb = str(e[1]) + \\\"b\\\"\n            \n            ya = str(e[2]) + \\\"a\\\"\n            yb = str(e[2]) + \\\"b\\\"\n            \n            if xa not in par:\n                par[xa] = xa\n                sz[xa] = 1\n            if xb not in par:\n                par[xb] = xb\n                sz[xb] = 1\n                \n            if ya not in par:\n                par[ya] = ya\n                sz[ya] = 1\n                \n            if yb not in par:\n                par[yb] = yb\n                sz[yb] = 1\n                \n            if t == 3:\n                if connected(xa, ya) and connected(xb, yb):\n                    count += 1\n                    continue\n                union(xa, ya)\n                union(xb, yb)\n            elif t == 2:\n                if connected(xb, yb):\n                    count += 1\n                    continue\n                union(xb, yb)\n            else:\n                if connected(xa, ya):\n                    count += 1\n                    continue\n                union(xa, ya)\n        \n        mxa = 0\n        mxb = 0\n        for x in sz:\n            if x[-1] == \\\"a\\\":\n                mxa = max(mxa, sz[x])\n            elif x[-1] == \\\"b\\\":\n                mxb = max(mxb, sz[x])\n                \n        if mxa == len(st) and mxb == len(st):\n            return count\n        return -1\n            \n                \n                \n                \n                    \n                \n                \n        \n        ", "\nclass DSU:\n    \n    def __init__(self, a):\n        self.par = {x:x for x in a}\n    \n    def merge(self, u, v):\n        rootu = self.find(u)\n        rootv = self.find(v)\n        \n        if rootu == rootv:\n            return False\n        \n        self.par[rootu] = rootv\n        return True\n    \n    def find(self, u):\n        if self.par[u] != u:\n            self.par[u] = self.find(self.par[u])\n        return self.par[u]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        dsu1 = DSU(range(1,n+1))\n        dsu2 = DSU(range(1,n+1))\n        \n        grouper = defaultdict(list)\n        for t,u,v in edges:\n            grouper[t].append([u,v])\n        \n        both, alice, bob = grouper[3], grouper[1], grouper[2]\n        \n        ret = 0\n        \n        for u,v in both:\n            if not dsu1.merge(u, v):\n                ret += 1\n            dsu2.merge(u, v)\n                \n        for u,v in alice:\n            if not dsu1.merge(u, v):\n                ret += 1\n        \n        for u,v in bob:\n            if not dsu2.merge(u, v):\n                ret += 1\n        \n        if len(set(dsu1.find(u) for u in dsu1.par)) != 1 or len(set(dsu2.find(u) for u in dsu2.par)) != 1:\n            return -1\n            \n        return ret", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        fa = [i for i in range(n+1)]\n        def find(x):\n            if fa[x]!=x:\n                fa[x]=find(fa[x])\n            return fa[x]\n        \n        def uni(x,y):\n            fx = find(x)\n            fy = find(y)\n            fa[fx] = fy\n        \n        res = 0\n        A = 0 # nodes Alice can go\n        B = 0 # nodes Bob can go\n        \n        #type 3\n        for t,u,v in edges:\n            if t==3:\n                fu = find(u)\n                fv = find(v)\n                if fu==fv:\n                    res+=1\n                else:\n                    uni(u,v)\n                    A+=1\n                    B+=1\n        \n        fa_copy = fa[:]\n        #edges Alice can go\n        for t,u,v in edges:\n            if t==1:\n                fu=find(u)\n                fv=find(v)\n                if fu==fv:\n                    res+=1\n                else:\n                    uni(u,v)\n                    A+=1\n        \n        fa = fa_copy #Bob can't use the graph of Alice\n        #edges bob can go\n        for t,u,v in edges:\n            if t==2:\n                fu=find(u)\n                fv=find(v)\n                if fu==fv:\n                    res+=1\n                else:\n                    uni(u,v)\n                    B+=1\n        \n        if A!=n-1 or B!=n-1:\n            return -1\n        \n        return res\n        \n", "from typing import *\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges.sort(key=lambda x: (-x[0]))\n        num_used = 0\n\n        # Using the UnionFind DS with idx 0 as the parent and idx 1 as the height\n        uf1 = [[i for i in range(n)]] + [[0] * n]\n        uf2 = [[i for i in range(n)]] + [[0] * n]\n\n        i = 0\n        while i < len(edges):\n            t, s, d = edges[i]\n            s -= 1\n            d -= 1\n\n            if t == 3:\n                if self.union_find_merge(uf1, s, d):\n                    num_used += 1\n                self.union_find_merge(uf2, s, d)\n            elif t == 2:\n                if self.union_find_merge(uf2, s, d):\n                    num_used += 1\n            else:\n                if self.union_find_merge(uf1, s, d):\n                    num_used += 1\n            i += 1\n\n        if self.find_num_components(n, uf1) > 1 or self.find_num_components(n, uf2) > 1:\n            return -1\n\n        return len(edges) - num_used\n\n    def find_num_components(self, n, uf):\n        num_components = 0\n        for idx in range(n):\n            parent = uf[0][idx]\n            if idx == parent:\n                num_components += 1\n        return num_components\n\n    def union_find_merge(self, uf, node1, node2):\n        p1 = self.union_find_get_parent(uf, node1)\n        p2 = self.union_find_get_parent(uf, node2)\n\n        if p1 == p2:\n            return False  # We can discard the edge as both have same parent\n\n        if uf[1][p1] > uf[1][p2]:\n            uf[0][p2] = p1\n        else:\n            uf[0][p1] = p2\n            uf[1][p2] = max(uf[1][p2], uf[1][p1] + 1)\n\n        return True\n\n    def union_find_get_parent(self, uf, node):\n        while uf[0][node] != node:\n            node = uf[0][node]\n        return node", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        size1 = [1] * n\n        size2 = [1] * n\n        par1 = [i for i in range(n)]\n        par2 = [i for i in range(n)]\n        distinct1 = [n]\n        distinct2 = [n]\n        \n        def find(node, par):\n            while par[node] != node:\n                par[node] = par[par[node]]\n                node = par[node]\n            return node\n        \n        def union(a,b,size,par,distinct):\n            root1 = find(a,par)\n            root2 = find(b,par)\n            \n            if root1 != root2:\n                if size[root1] < size[root2]:\n                    par[root1] = par[root2]\n                    size[root2] += size[root1]\n                else:\n                    par[root2] = par[root1]\n                    size[root1] += size[root2]\n                distinct[0]-=1\n                return True\n            return False\n            \n        edges.sort(key=lambda x:x[0],reverse=True)\n        edges_needed = 0\n        for i in edges:\n            type = i[0]\n            u = i[1]-1\n            v = i[2]-1\n            if type == 3:\n                a = union(u,v,size1,par1,distinct1)\n                b = union(u,v,size2,par2,distinct2)\n                if a or b:\n                    edges_needed+=1\n            elif type == 1:\n                if union(u,v,size1,par1,distinct1):\n                    edges_needed+=1\n            else:\n                # print(u,v)\n                if union(u,v,size2,par2,distinct2):\n                    edges_needed+=1\n                # print(par2)\n        # print(par1,par2)        \n        if distinct1[0] != 1 or distinct2[0] != 1:\n            return -1\n        return len(edges) - edges_needed\n                \n        \n        \n", "class Solution:\n    def find(self, x, uf):\n        if uf.get(x) == None:\n            uf[x] = x\n            return x\n        if uf[x] != x:\n            uf[x] = self.find(uf[x], uf)\n        return uf[x]\n    \n    def union(self, x, y, uf):\n        root1 = self.find(x, uf)\n        root2 = self.find(y, uf)\n        if root1 == root2:\n            return False\n        uf[root1] = root2\n        return True\n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges.sort(reverse=True)\n        uf1 = {}\n        uf2 = {}\n        check1 = [False] * n\n        check2 = [False] * n\n        need = 0\n        for t, n1, n2 in edges:\n            if t == 3:\n                val1 = self.union(n1, n2, uf1) \n                val2 = self.union(n1, n2, uf2)\n                if val1 or val2:\n                    need += 1\n            elif t == 1:\n                if self.union(n1, n2, uf1):\n                    need += 1\n            elif t == 2:\n                if self.union(n1, n2, uf2):\n                    need += 1\n        \n        if len(uf1) != n:\n            \n            return -1\n        if len(uf2) != n:\n            \n            return -1\n        \n        uf1_a = [(i, key) for i, key in enumerate(uf1)]\n        uf2_a = [(i, key) for i, key in enumerate(uf2)]\n        for i1, key1 in uf1_a:\n            \n            if i1 == 0:\n                root1 = self.find(key1, uf1)\n            else:\n                if self.find(key1, uf1) != root1:\n                   \n                    return -1\n        \n        for i2, key2 in uf2_a:\n            if i2 == 0:\n                root2 = self.find(key2, uf2)\n            else:\n                if self.find(key2, uf2) != root2:\n                   \n                    return -1\n        \n        return len(edges) - need\n                    \n", "class Solution:\n    \n    class DisjointSet:\n        def __init__(self, n):\n            self.parents = [i for i in range(n+1)]\n            self.ranks = [0 for i in range(n+1)]\n            \n        def parent(self, node):\n            if self.parents[node] != node:\n                self.parents[node] = self.parent(self.parents[node])\n            return self.parents[node]\n        \n        def join(self, node1, node2):\n            p1 = self.parent(node1)\n            p2 = self.parent(node2)\n            \n            r1 = self.ranks[p1]\n            r2 = self.ranks[p2]\n            \n            if r1 < r2:\n                self.parents[p1] = p2\n            elif r2 < r1:\n                self.parents[p2] = p1\n            else:\n                self.parents[p1] = p2\n                self.ranks[p1] += 1\n    \n        def is_connected(self, node1, node2):\n            return self.parent(node1) == self.parent(node2)\n    \n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        ans = 0\n        \n        graph = [dict() for i in range(n+1)]\n        \n        for edge in edges:            \n            if edge[2] not in graph[edge[1]]:\n                graph[edge[1]][edge[2]] = edge[0]\n            else:\n                if edge[0] == 3:\n                    if graph[edge[1]][edge[2]] == 4:\n                        ans += 2\n                    else:\n                        ans += 1\n                    graph[edge[1]][edge[2]] = 3\n                elif graph[edge[1]][edge[2]] == 3:\n                    ans += 1\n                    graph[edge[1]][edge[2]] = 3\n                else:\n                    graph[edge[1]][edge[2]] = 4 \n                \n        \n        alice_dset = Solution.DisjointSet(n)\n        \n        print(ans)\n        \n        for i in range(1, n+1):\n            for (j, t) in list(graph[i].items()):\n                if t == 3:\n                    if not alice_dset.is_connected(i, j):\n                        alice_dset.join(i, j)\n                    else:\n                        ans += 1\n        bob_dset = copy.deepcopy(alice_dset)\n        \n        for i in range(1, n+1):\n            for (j, t) in list(graph[i].items()):\n                print((i, j, t))\n                if t == 1 or t == 4:\n                    if not alice_dset.is_connected(i, j):\n                        alice_dset.join(i, j)\n                    else:\n                        ans += 1\n                if t == 2 or t == 4:\n                    if not bob_dset.is_connected(i, j):\n                        bob_dset.join(i, j)\n                    else:\n                        ans += 1\n                        \n        for i in range(1, n):\n            if not bob_dset.is_connected(i, i + 1) or not alice_dset.is_connected(i, i + 1):\n                # print(i, bob_dset.parent(i), bob_dset.parent(i + 1))\n                # print(i, alice_dset.parent(i), alice_dset.parent(i + 1))\n                \n                return -1\n        \n        return ans\n        \n", "class UnionFindSet:\n    def __init__(self, n):\n        self.parents = list(range(n))\n        self.ranks = [1] * n\n        self.size = 1\n        \n    def find(self, u):\n        if u != self.parents[u]:\n            self.parents[u] = self.find(self.parents[u])\n        return self.parents[u]\n    \n    def union(self, u, v):\n        pu, pv = self.find(u), self.find(v)\n        if pu == pv:\n            return False\n        if self.ranks[pu] > self.ranks[pv]:\n            self.parents[pv] = pu\n        elif self.ranks[pv] > self.ranks[pu]:\n            self.parents[pu] = pv\n        else:\n            self.parents[pu] = pv\n            self.ranks[pv] += 1\n        self.size += 1\n        return True\n    \n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1, uf2, ans = UnionFindSet(n), UnionFindSet(n), 0\n\\t\\t\n        for t, u, v in edges:\n            if t != 3:\n                continue\n            if not uf1.union(u - 1, v - 1) or not uf2.union(u - 1, v - 1):\n                ans += 1\n        \n        for t, u, v in edges:\n            if t == 1 and not uf1.union(u - 1, v - 1):\n                ans += 1\n            elif t == 2 and not uf2.union(u - 1, v - 1):\n                ans += 1\n   \n        return ans if uf1.size == n and uf2.size == n else -1\n# class Solution:\n#     def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n#         self.father_alice = [i for i in range(n + 1)]\n#         self.father_bob = [i for i in range(n + 1)]\n#         res = 0\n#         edge_alice, edge_bob = 0, 0\n#         for type, u, v in edges:\n#             if type == 3:\n#                 if self.connect(u, v, True) == 1:\n#                     edge_alice += 1\n#                     edge_bob += 1\n#                 else:\n#                     res += 1\n                \n#                 self.connect(u, v, False)\n        \n#         for type, u, v in edges:\n#             if type == 1:\n#                 if self.connect(u, v, True) == 1:\n#                     edge_alice += 1\n#                 else:\n#                     res += 1\n#             elif type == 2:\n#                 if self.connect(u, v, False) == 1:\n#                     edge_bob += 1\n#                 else:\n#                     res += 1\n        \n#         if edge_alice == edge_bob == n - 1:\n#             return res\n#         return -1\n    \n    \n    \n#     def find(self, x, is_alice):\n#         if is_alice:\n#             if self.father_alice[x] == x:\n#                 return self.father_alice[x]\n#             self.father_alice[x] = self.find(self.father_alice[x], True)\n#             return self.father_alice[x]\n#         else:\n#             if self.father_bob[x] == x:\n#                 return self.father_bob[x]\n#             self.father_bob[x] = self.find(self.father_bob[x], False)\n#             return self.father_bob[x]\n    \n#     def connect(self, a, b, is_alice):\n#         if is_alice:\n#             root_a = self.find(a, True)\n#             root_b = self.find(b, True)\n#             if root_a == root_b:\n#                 return 0\n#             else:\n#                 self.father_alice[max(root_a, root_b)] = min(root_a, root_b)\n#                 return 1\n#         else:\n#             root_a = self.find(a, False)\n#             root_b = self.find(b, False)\n#             if root_a == root_b:\n#                 return 0\n#             else:\n#                 self.father_bob[max(root_a, root_b)] = min(root_a, root_b)\n#                 return 1\n        \n        \n        \n        \n        \n#         self.father_alice = [i for i in range(n + 1)]\n#         self.father_bob = [i for i in range(n + 1)]\n        \n#         res = 0\n#         for type, u, v in edges:\n#             if type == 3:\n#                 res += self.connect(u, v, True)\n#                 self.connect(u, v, False)\n        \n#         for type, u, v in edges:\n#             if type == 1:\n#                 res += self.connect(u, v, True)\n#             elif type == 2:\n#                 res += self.connect(u, v, False)\n        \n        \n#         if self.check_valid(True) and self.check_valid(False):\n#             return res\n#         return -1\n    \n    \n#     def find(self, x, is_alice):\n#         if is_alice:\n#             if self.father_alice[x] == x:\n#                 return self.father_alice[x]\n#             self.father_alice[x] = self.find(self.father_alice[x], True)\n#             return self.father_alice[x]\n        \n#         else:\n#             if self.father_bob[x] == x:\n#                 return self.father_bob[x]\n#             self.father_bob[x] = self.find(self.father_bob[x], False)\n#             return self.father_bob[x]\n        \n#     def connect(self, a, b, is_alice):\n#         if is_alice:\n#             root_a = self.find(a, True)\n#             root_b = self.find(b, True)\n#             if root_a != root_b:\n#                 self.father_alice[max(root_a, root_b)] = min(root_a, root_b)\n#                 return 0\n#             return 1\n        \n#         else:\n#             root_a = self.find(a, False)\n#             root_b = self.find(b, False)\n#             if root_a != root_b:\n#                 self.father_bob[max(root_a, root_b)] = min(root_a, root_b)\n#                 return 0\n#             return 1\n        \n#     def check_valid(self, is_alice):\n#         if is_alice:\n#             root = self.find(1, True)\n#             for i in range(1, len(self.father_alice)):\n#                 if self.find(i, True) != root:\n#                     return False\n#             return True\n        \n#         else:\n#             root = self.find(1, False)\n#             for i in range(1, len(self.father_bob)):\n#                 if self.find(i, False) != root:\n#                     return False\n#             return True", "\nclass DSU:\n    def __init__(self):\n        self.parent = {}\n        self.size = {}\n        \n    def root(self,A):\n        tmp = A\n        while self.parent[tmp]!=tmp:\n            tmp = self.parent[tmp]\n            self.parent[tmp] = self.parent[self.parent[tmp]]\n        return tmp\n    \n    def union(self,A,B):\n        if self.root(A)==self.root(B):\n            return False\n        else:\n            if self.size[self.root(A)] >= self.size[self.root(B)]:\n                self.size[self.root(A)]+= self.size[self.root(B)]\n                self.parent[self.root(B)] = self.root(A)\n            else:\n                self.size[self.root(B)]+= self.size[self.root(A)]\n                self.parent[self.root(A)] = self.root(B)\n            return True\n    \n    def add_edge(self,A,B):\n        if A not in self.parent:\n            self.parent[A] = A\n            self.size[A] = 1\n        if B not in self.parent:\n            self.parent[B] = B\n            self.size[B] = 1\n        return self.union(A,B)\n\n    def get_node_count(self):\n        return len(self.parent)\n                 \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        # Spanning Tree for Type 3 edges\n        \n        edges_removed = 0\n        \n        alice_graph,bob_graph = DSU(),DSU()\n        \n        for typ,u,v in edges:\n            if typ==3:\n                if not (alice_graph.add_edge(u,v) and bob_graph.add_edge(u,v)):\n                    edges_removed+=1\n        \n        # Spanning Tree for Type 2 and Type 3 edges\n        \n        for typ,u,v in edges:\n            if typ==2:\n                if not bob_graph.add_edge(u,v):\n                    edges_removed+=1\n            if typ==1:\n                if not alice_graph.add_edge(u,v):\n                    edges_removed+=1\n        \n        # print('alice_graph',alice_graph.parent)\n        # print('bob_graph',bob_graph.parent)\n        \n        if alice_graph.get_node_count()!=n or bob_graph.get_node_count()!=n:\n            return -1\n                    \n        return edges_removed\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def get_color(u):\n            out = colors[u]\n            if out != u:                \n                out = get_color(out)\n                colors[u] = out\n            return out\n                            \n        ans = 0\n        colors = [i for i in range(n+1)]\n        edges_3 = 0\n        for one in edges:\n            if one[0] == 3:\n                u_color = get_color(one[1])\n                v_color = get_color(one[2])\n                \n                if u_color == v_color:\n                    ans += 1\n                else:\n                    edges_3 += 1\n                    colors[v_color] = u_color\n                    \n        colors2 = list(colors)\n        edges_count = edges_3\n        for one in edges:\n            if one[0] == 1:\n                u_color = get_color(one[1])\n                v_color = get_color(one[2])\n                if u_color == v_color:\n                    ans += 1\n                else:                \n                    edges_count += 1\n                    colors[v_color] = u_color\n                \n        if edges_count < n - 1:\n            return -1\n        \n        # print(edges_count)\n        # print(colors)\n        \n        colors = colors2\n        edges_count = edges_3\n        for one in edges:\n            if one[0] == 2:\n                u_color = get_color(one[1])\n                v_color = get_color(one[2])\n                if u_color == v_color:\n                    ans += 1\n                else:\n                    edges_count += 1\n                    colors[v_color] = u_color\n                \n        if edges_count < n - 1:\n            return -1\n                        \n        return ans", "class Solution:\n    def __init__(self):\n        self.n = 0\n        self.used = []\n    def dfs(self,edges,i):\n        self.used[i] = True\n        for edge in edges[i]:\n            if self.used[edge] == False:\n                self.dfs(edges,edge)\n    def iterate(self,edges):\n        self.used = [False for i in range(self.n)]\n        components = 0\n        for i in range(self.n):\n            if self.used[i] == True:\n                continue\n            self.dfs(edges,i)\n            components += 1\n        return components\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        self.n = n\n        alice = [[] for i in range(self.n)]\n        bob = [[] for i in range(self.n)]\n        both = [[] for i in range(self.n)]\n        bothCount = 0\n        tot = len(edges)\n        for edge in edges:\n            if edge[0] == 1:\n                alice[edge[1]-1].append(edge[2]-1)\n                alice[edge[2]-1].append(edge[1]-1)\n            if edge[0] == 2:\n                bob[edge[1]-1].append(edge[2]-1)\n                bob[edge[2]-1].append(edge[1]-1)\n            if edge[0] == 3:\n                bob[edge[1]-1].append(edge[2]-1)\n                bob[edge[2]-1].append(edge[1]-1)\n                alice[edge[1]-1].append(edge[2]-1)\n                alice[edge[2]-1].append(edge[1]-1)\n                both[edge[1]-1].append(edge[2]-1)\n                both[edge[2]-1].append(edge[1]-1)\n                bothCount += 1\n        if self.iterate(alice) != 1 or self.iterate(bob) != 1:\n            return -1\n        bothComponents = self.iterate(both)\n        needed = self.n - bothComponents\n        needed += 2*(bothComponents-1)\n        return tot - needed", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # Union find\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n\n        res = e1 = e2 = 0\n\n        # Alice and Bob\n        root = [*list(range(n + 1))]\n        for t, i, j in edges:\n            if t == 3:\n                if uni(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        root0 = root[:]\n        \n        root = root0[:]\n        # only Alice\n        for t, i, j in edges:\n            if t == 1:\n                if uni(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n\n        # only Bob\n        root = root0[:]\n        for t, i, j in edges:\n            if t == 2:\n                if uni(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if e1 == e2 == n - 1 else -1\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        dic = {}\n        type1 = 0\n        type2 = 0\n        type3 = 0\n        for edge in edges:\n            etype, a, b = edge\n            if a in dic:\n                if b in dic[a]:\n                    dic[a][b].add(etype)\n                else:\n                    dic[a][b] = set([etype])\n            else:\n                dic[a] = {b: set([etype])}\n            if b in dic:\n                if a in dic[b]:\n                    dic[b][a].add(etype)\n                else:\n                    dic[b][a] = set([etype])\n            else:\n                dic[b] = {a: set([etype])}\n            if etype == 1:\n                type1 += 1\n            elif etype == 2:\n                type2 += 1\n            else:\n                type3 += 1\n                \n                    \n        res = 0         \n        seen_A = [0] * n\n        def dfs_A(i):\n            seen_A[i - 1] = 1\n            if i in dic:\n                for j in dic[i]:\n                    if (1 in dic[i][j] or 3 in dic[i][j]) and seen_A[j - 1] == 0:\n                        dfs_A(j)\n        seen_B = [0] * n\n        def dfs_B(i):\n            seen_B[i - 1] = 1\n            if i in dic:\n                for j in dic[i]:\n                    if (2 in dic[i][j] or 3 in dic[i][j]) and seen_B[j - 1] == 0:\n                        dfs_B(j)\n        \n        dfs_A(1)\n        if sum(seen_A) != n:\n            return -1\n        dfs_B(1)\n        if sum(seen_B) != n:\n            return -1\n                        \n        seen_3 = [0] * n\n        def dfs_3(i):\n            seen_3[i - 1] = 1\n            self.cnt += 1\n            if i in dic:\n                for j in dic[i]:\n                    if (3 in dic[i][j]) and seen_3[j - 1] == 0:\n                        dfs_3(j)\n        \n        tmp = 0\n        self.cnt = 0\n        tmp_n = 0\n        for i in range(1, n + 1):\n            if seen_3[i - 1] == 0:\n                tmp_n += 1\n                self.cnt = 0\n                dfs_3(i)\n                tmp += self.cnt - 1\n                \n        \n        \n        res += type3 - tmp\n            \n        return res + type1 + type2 - (tmp_n - 1) - (tmp_n - 1)\n                \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        dic = {}\n        type1 = 0\n        type2 = 0\n        type3 = 0\n        for edge in edges:\n            etype, a, b = edge\n            if a in dic:\n                if b in dic[a]:\n                    dic[a][b].add(etype)\n                else:\n                    dic[a][b] = set([etype])\n            else:\n                dic[a] = {b: set([etype])}\n            if b in dic:\n                if a in dic[b]:\n                    dic[b][a].add(etype)\n                else:\n                    dic[b][a] = set([etype])\n            else:\n                dic[b] = {a: set([etype])}\n            if etype == 1:\n                type1 += 1\n            elif etype == 2:\n                type2 += 1\n            else:\n                type3 += 1\n                \n                    \n        res = 0\n        # for key, val in dic.items():\n        #     for keyp, valp in val.items():\n        #         if 3 in valp:\n        #             if 1 in valp:\n        #                 type1 -= 1\n        #                 dic[key][keyp].remove(1)\n        #                 res += 1\n        #             if 2 in valp:\n        #                 type2 -= 1\n        #                 dic[key][keyp].remove(2)\n        #                 res += 1\n                        \n        seen_A = [0] * n\n        def dfs_A(i):\n            seen_A[i - 1] = 1\n            if i in dic:\n                for j in dic[i]:\n                    if (1 in dic[i][j] or 3 in dic[i][j]) and seen_A[j - 1] == 0:\n                        dfs_A(j)\n        seen_B = [0] * n\n        def dfs_B(i):\n            seen_B[i - 1] = 1\n            if i in dic:\n                for j in dic[i]:\n                    if (2 in dic[i][j] or 3 in dic[i][j]) and seen_B[j - 1] == 0:\n                        dfs_B(j)\n        \n        dfs_A(1)\n        if sum(seen_A) != n:\n            return -1\n        dfs_B(1)\n        if sum(seen_B) != n:\n            return -1\n                        \n        seen_3 = [0] * n\n        def dfs_3(i):\n            seen_3[i - 1] = 1\n            self.cnt += 1\n            if i in dic:\n                for j in dic[i]:\n                    if (3 in dic[i][j]) and seen_3[j - 1] == 0:\n                        dfs_3(j)\n        \n        tmp = 0\n        self.cnt = 0\n        tmp_n = 0\n        for i in range(1, n + 1):\n            if seen_3[i - 1] == 0:\n                tmp_n += 1\n                self.cnt = 0\n                dfs_3(i)\n                tmp += self.cnt - 1\n                \n        \n        \n        res += type3 - tmp\n            \n        return res + type1 + type2 - (tmp_n - 1) - (tmp_n - 1)\n                \n", "class DisjointSet:\n    def __init__(self, elements):\n        self.parent = [i for i in range(elements)]\n        self.size = [1] * elements\n    def find(self, value):\n        if self.parent[value] != value:\n            self.parent[value] = self.find(self.parent[value])\n        return self.parent[value]\n    def union(self, value1, value2):\n        parent1, parent2 = self.find(value1), self.find(value2)\n        if parent1 == parent2:\n            return True\n        if self.size[parent1] > self.size[parent2]:\n            self.parent[parent2] = parent1\n            self.size[parent1] += self.size[parent2]\n        else:\n            self.parent[parent1] = parent2\n            self.size[parent2] += self.size[parent1]\n        return False\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        graph = [list() for i in range(n + 1)]\n        for t, u, v in edges:\n            graph[u].append([v, t])\n            graph[v].append([u, t])\n        def dfs(source, ty):\n            nonlocal cnt\n            cnt += 1\n            vis[source] = 1\n            for child, typer in graph[source]:\n                if typer in [ty, 3] and not vis[child]:\n                    dfs(child, ty)\n        cnt = 0\n        vis = [0] * (n + 1)\n        dfs(1, 1)\n        if cnt != n:\n            return -1\n        vis = [0] * (n + 1)\n        cnt = 0\n        dfs(1, 2)\n        if cnt != n:\n            return -1\n        answer = 0\n        dsu1, dsu2 = DisjointSet(n + 1), DisjointSet(n + 1)\n        for t, u, v in edges:\n            if t == 3:\n                dsu1.union(u, v)\n                if dsu2.union(u, v):\n                    answer += 1\n        for t, u, v in edges:\n            if t == 1:\n                if dsu1.union(u, v):\n                    answer += 1\n            if t == 2:\n                if dsu2.union(u, v):\n                    answer += 1\n        return answer\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n\\t\\t# process graph\n        graphs = [collections.defaultdict(list) for _ in range(3)]\n        for c, i, j in edges:\n            graphs[c-1][i].append((-c, j))\n            graphs[c-1][j].append((-c, i))\n\\t\\t# build tree for Alice\n        e = graphs[2][1] + graphs[0][1]\n        heapq.heapify(e)\n        treeset = set([1])\n        type3 = 0\n        while e:\n            c, y = heapq.heappop(e)\n            if y not in treeset:\n                treeset.add(y)\n                if c == -3:\n                    type3 += 1\n                for item in graphs[2][y]:\n                    heapq.heappush(e, item)\n                for item in graphs[0][y]:\n                    heapq.heappush(e, item)\n        if len(treeset) != n:\n            return -1\n\\t\\t# build tree for Bob\n        e = graphs[2][1] + graphs[1][1]\n        heapq.heapify(e)\n        treeset = set([1])\n        while e:\n            c, y = heapq.heappop(e)\n            if y not in treeset:\n                treeset.add(y)\n                for item in graphs[2][y]:\n                    heapq.heappush(e, item)\n                for item in graphs[1][y]:\n                    heapq.heappush(e, item)\n        if len(treeset) != n:\n            return -1\n        return len(edges) + type3 - 2 * (n - 1)", "class DisjointSet:\n    def __init__(self, elements):\n        self.parent = [i for i in range(elements)]\n        self.size = [1] * elements\n    def find(self, value):\n        if self.parent[value] != value:\n            self.parent[value] = self.find(self.parent[value])\n        return self.parent[value]\n    def union(self, value1, value2):\n        parent1, parent2 = self.find(value1), self.find(value2)\n        if parent1 == parent2:\n            return True\n        if self.size[parent1] > self.size[parent2]:\n            self.parent[parent2] = parent1\n            self.size[parent1] += self.size[parent2]\n        else:\n            self.parent[parent1] = parent2\n            self.size[parent2] += self.size[parent1]\n        return False\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        graph = [list() for i in range(n + 1)]\n        extra = 0\n        for t, u, v in edges:\n            graph[u].append([v, t])\n            graph[v].append([u, t])\n        def dfs(source, ty):\n            nonlocal cnt\n            cnt += 1\n            vis[source] = 1\n            for child, typer in graph[source]:\n                if typer in [ty, 3] and not vis[child]:\n                    dfs(child, ty)\n        # To Check if Alice can visit all nodes.\n        cnt = 0\n        vis = [0] * (n + 1)\n        dfs(1, 1)\n        if cnt != n:\n            return -1\n        # To check if Bob can visit all nodes.\n        vis = [0] * (n + 1)\n        cnt = 0\n        dfs(1, 2)\n        if cnt != n:\n            return -1\n        answer = 0\n        dsu1, dsu2 = DisjointSet(n + 1), DisjointSet(n + 1)\n        for t, u, v in edges:\n            if t == 3:\n                dsu1.union(u, v)\n                if dsu2.union(u, v):\n                    answer += 1\n        for t, u, v in edges:\n            if t == 1:\n                if dsu1.union(u, v):\n                    answer += 1\n            if t == 2:\n                if dsu2.union(u, v):\n                    answer += 1\n        return answer\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        graph = collections.defaultdict(list)\n        for t, s, e in edges:\n            graph[s].append((-t, e))\n            graph[e].append((-t, s))\n                \n        root = [1]\n        candidates = []\n        for t, e in graph[1]:\n            heapq.heappush(candidates, (t, e))\n            \n        A = set(root)\n        B = set(root)\n        \n        ans = 0\n        while candidates:\n            t, e = heapq.heappop(candidates)\n            if -t == 1:\n                if e in A: continue\n                A.add(e)\n                ans += 1\n                for nt, ne in graph[e]:\n                    heapq.heappush(candidates, (nt, ne))\n            elif -t == 2:\n                if e in B: continue\n                B.add(e)\n                ans += 1\n                for nt, ne in graph[e]:\n                    heapq.heappush(candidates, (nt, ne))\n            else:\n                if e in A and e in B: continue\n                A.add(e)\n                B.add(e)\n                ans += 1\n                for nt, ne in graph[e]:\n                    heapq.heappush(candidates, (nt, ne))\n        \n        if len(A) == n and len(B) == n: return len(edges) - ans\n        return -1\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        a_uf = UF(n+1)\n        b_uf = UF(n+1)\n        unwanted = 0\n\n        for t, u, v in edges:    \n            if t == 3:            \n                # Alice and Bob\n                if a_uf.find(u) == a_uf.find(v) and b_uf.find(u) == b_uf.find(v):\n                    # both guys dont need\n                    unwanted += 1\n                else:\n                    a_uf.union(u, v)\n                    b_uf.union(u, v)\n        \n        for t, u, v in edges:\n            #print((t, u, v))\n            if t == 1:\n                # Alice\n                if a_uf.find(u) == a_uf.find(v):\n                    # dont need this\n                    unwanted += 1\n                else:\n                    a_uf.union(u, v)\n        for t, u, v in edges:    \n            if t == 2:\n                # Bob\n                if b_uf.find(u) == b_uf.find(v):\n                    # dont need this\n                    unwanted += 1\n                else:\n                    b_uf.union(u, v)\n                \n        if a_uf.size[a_uf.find(1)] < n or b_uf.size[b_uf.find(1)] < n:\n            return -1\n                \n        return unwanted\n    \nclass UF:\n    def __init__(self, n):\n        self.uf = [i for i in range(n)]\n        self.size = [1] * n\n    \n    def find(self, u):\n        while u != self.uf[u]:\n            self.uf[u] = self.uf[self.uf[u]]\n            u = self.uf[u]\n        return u\n    \n    def union(self, u, v):\n        rootU = self.find(u)\n        rootV = self.find(v)\n        if rootU != rootV:\n            if self.size[rootU] > self.size[rootV]:\n                self.size[rootU] += self.size[rootV]\n                self.uf[rootV] = rootU\n            else:\n                self.size[rootV] += self.size[rootU]\n                self.uf[rootU] = rootV\n        \n        \n", "class Graph:\n    def __init__(self):\n        self.vertices = set()\n        self.bi_edges = set()\n        self.uni_edges = set()\n    \n    def find(self, parent, u):\n        if parent[u] == u:\n            return u\n        return self.find(parent, parent[u])\n    \n    \n    def union(self, parent, rank, u, v):\n        \n        u_root = self.find(parent, u)\n        v_root = self.find(parent, v)\n        \n        if rank[u_root] < rank[v_root]:\n            parent[u_root] = v_root\n        elif rank[u_root] > rank[v_root]:\n            parent[v_root] = u_root\n        else:\n            parent[v_root] = u_root\n            rank[u_root] += 1\n        return 0\n        \n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        print(\\\"total edges\\\", len(edges))\n        # print(sorted(edges, key=lambda x : x[0]))\n        graph1 = Graph()\n        graph2 = Graph()\n        \n        #create 2 graphs\n        for edge in edges:\n            edge_type = edge[0]\n            u = edge[1]\n            v = edge[2]\n            \n            if edge[0]==1:\n                graph1.vertices.add(u)\n                graph1.vertices.add(v)\n                graph1.uni_edges.add((u,v))\n            elif edge[0] == 2:\n                graph2.vertices.add(u)\n                graph2.vertices.add(v)\n                graph2.uni_edges.add((u,v))\n            else:\n                graph1.vertices.add(u)\n                graph1.vertices.add(v)\n                graph1.bi_edges.add((u,v))\n                \n                graph2.vertices.add(u)\n                graph2.vertices.add(v)\n                graph2.bi_edges.add((u,v))\n        \n        if len(graph1.vertices) < n or len(graph2.vertices) < n:\n            return -1\n        \n        \n        print(\\\"edges in graph\\\",len(graph1.bi_edges)+len(graph1.uni_edges)+len(graph2.uni_edges))\n        deleted = 0\n\n        #detect cycle for given graph\n        # return count of deleted uni_edges and cycle_creating bi_edges\n        def minimum_spanning_tree(graph):\n            parent = {}\n            rank = {}\n            \n            for node in range(1,n+1):\n                parent[node] = node\n                rank[node] = 0\n            \n            cycle_bi_edges = set()\n            for edge in graph.bi_edges:\n                u = edge[0]\n                v = edge[1]\n                \n                if graph.find(parent, u) != graph.find(parent, v):\n                    graph.union(parent, rank, u, v)\n                else:\n                    cycle_bi_edges.add(edge)\n            \n            delete = 0\n            for edge in graph.uni_edges:\n                u = edge[0]\n                v = edge[1]\n                \n                if graph.find(parent, u) != graph.find(parent, v):\n                    graph.union(parent, rank, u, v)\n                else:\n                    delete += 1\n            # print(\\\"span\\\", delete, len(cycle_bi_edges))\n            return delete, cycle_bi_edges\n        \n        result1 = minimum_spanning_tree(graph1)\n        \n        result2 = minimum_spanning_tree(graph2)\n        \n        deleted = deleted + result1[0] + result2[0]\n        \n        delete_bi = 0\n        for edge in result1[1]:\n            u = edge[0]\n            v = edge[1]\n            \n            if (u,v) in result2[1]:\n                delete_bi += 1\n            \n            if (v,u) in result2[1]:\n                delete_bi +=1\n        print(delete_bi)\n        return deleted+delete_bi\n                \n                \n                \n            \n        ", "class UF:\n    def __init__(self):\n        self.d = defaultdict(int)\n        \n    def findRoot(self, key):\n        if self.d[key] > 0:\n            self.d[key] = self.findRoot(self.d[key])\n            return self.d[key]\n        else:\n            return key\n        \n    def mergeRoot(self, k1, k2):\n        r1, r2 = self.findRoot(k1), self.findRoot(k2)  \n        if r1 != r2:\n            r1, r2 = min(r1, r2), max(r1, r2)\n            self.d[r1] += self.d[r2]\n            self.d[r2] = r1\n        return r1\n    \n    def getSize(self, key):\n        return self.d[self.findRoot(key)]\n\nimport heapq\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        Edges = []\n        [heapq.heappush(Edges, (-e[0], e[1], e[2])) for e in edges]\n        uf1 = UF()\n        uf2 = UF()\n        \n        ttl = 0\n        while len(Edges) != 0:\n            t, src, dst = heapq.heappop(Edges)\n            \n            if t == -1:\n                if uf1.findRoot(src) == uf1.findRoot(dst):\n                    ttl += 1\n                    continue\n                else:\n                    uf1.d[uf1.mergeRoot(src, dst)] -= 1\n            if t == -2:\n                if uf2.findRoot(src) == uf2.findRoot(dst):\n                    ttl += 1\n                    continue\n                else:\n                    uf2.d[uf2.mergeRoot(src, dst)] -= 1\n                    \n            if t == -3:\n                if uf1.findRoot(src) == uf1.findRoot(dst) and uf2.findRoot(src) == uf2.findRoot(dst):\n                    ttl += 1\n                    continue\n                else:\n                    uf1.d[uf1.mergeRoot(src, dst)] -= 1\n                    uf2.d[uf2.mergeRoot(src, dst)] -= 1\n                    \n        if uf1.d[1] != - n + 1 or uf2.d[1] != - n + 1:\n            return -1\n        \n        return ttl", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, e: List[List[int]]) -> int:\n        def union(UF, u, v):\n            pu, pv = find(UF, u), find(UF, v)\n            if pu != pv: UF[pv] = pu\n        def find(UF, u):\n            if UF[u] != u: UF[u] = find(UF, UF[u])\n            return UF[u]         \n        def check(UF, t):            \n            UF = UF.copy()\n            for tp, u, v in e:\n                if tp != t: continue\n                pu, pv = find(UF, u), find(UF, v)\n                if pu == pv: self.ans += 1\n                else: union(UF, u, v)\n            return len(set(find(UF, u) for u in UF)) == 1\n        \n        self.ans, UF = 0, {u: u for u in range(1, n+1)}        \n        for t, u, v in e:\n            if t != 3: continue\n            pu, pv = find(UF, u), find(UF, v)\n            if pu == pv: self.ans += 1\n            else: union(UF, u, v)        \n        if not check(UF, 1) or not check(UF, 2): return -1        \n        return self.ans                        ", "from typing import Tuple\nclass Solution:\n    def __init__(self):\n      self.roots = {}\n      self.ranks = {}\n      self.groups = 0\n    \n    def find(self, node_info: Tuple[int, int]):\n      self.roots.setdefault(node_info, node_info)\n      self.ranks.setdefault(node_info, 1)\n      if self.roots[node_info] != node_info:\n        self.roots[node_info] = self.find(self.roots[node_info])\n      return self.roots[node_info]\n    \n    def union(self, node_info1, node_info2) -> bool:  # returns if the edge can be removed\n      root1, root2 = self.find(node_info1), self.find(node_info2)\n      if root1 != root2:\n        self.groups -= 1\n        self.roots[root2] = root1\n        return False  # we can't remove this edge because it's used\n      else:\n        return True  # we can remove this edge because there already is a path for these 2 nodes.\n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n      \\\"\\\"\\\"\n      Union find for alice and bob separately.\n      \\\"\\\"\\\"\n      edges.sort(key=lambda edge: -edge[0])\n      removes = 0\n      self.groups = n * 2\n      for tp, n1, n2 in edges:\n        can_remove = False\n        if tp == 1:\n          can_remove = self.union((1, n1), (1, n2))\n        elif tp == 2:\n          can_remove = self.union((2, n1), (2, n2))\n        else:\n          can_remove1, can_remove2 = self.union((1, n1), (1, n2)), self.union((2, n1), (2, n2))\n          can_remove = can_remove1 and can_remove2\n        removes += (1 if can_remove else 0)  \n      \n      # If in the end both alice and alice have a single group, then return removed count\n      return removes if self.groups == 2 else -1 ", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        n_a={}\n        n_b={}\n        for e in edges:\n            if e[0]==1 or e[0]==3:\n                if e[1] not in n_a:\n                    n_a[e[1]]=[]\n                n_a[e[1]].append(e[2])\n                if e[2] not in n_a:\n                    n_a[e[2]]=[]\n                n_a[e[2]].append(e[1])\n            if e[0]==2 or e[0]==3:\n                if e[1] not in n_b:\n                    n_b[e[1]]=[]\n                n_b[e[1]].append(e[2])\n                if e[2] not in n_b:\n                    n_b[e[2]]=[]\n                n_b[e[2]].append(e[1])\n                \n        visited=set()\n        l=list(n_b.keys())\n        start=l[0]\n        visited.add(start)\n        q=[start]\n        while q:\n            actual=q[0]\n            del q[0]\n            if actual in n_a:\n                for nb in n_a[actual]:\n                    if nb not in visited:\n                        visited.add(nb)\n                        q.append(nb)\n        if len(visited)!=n:\n            return -1\n        \n        visited=set()\n        l=list(n_b.keys())\n        start=l[0]\n        visited.add(start)\n        q=[start]\n        while q:\n            actual=q[0]\n            del q[0]\n            if actual in n_b:\n                for nb in n_b[actual]:\n                    if nb not in visited:\n                        visited.add(nb)\n                        q.append(nb)\n        if len(visited)!=n:\n            return -1        \n\n        \n        \n        \n        \n        \n        \n        parent_a={}\n        parent_b={}\n        for i in range(1,n+1):\n            parent_a[i]=i\n            parent_b[i]=i\n            \n        def find_a(x):\n            if parent_a[x]!=x:\n                parent_a[x]=find_a(parent_a[x])\n            return parent_a[x]\n        def find_b(x):\n            if parent_b[x]!=x:\n                parent_b[x]=find_b(parent_b[x])\n            return parent_b[x]\n        def union_a(x,y):\n            x=find_a(x)\n            y=find_a(y)\n            if x!=y:\n                parent_a[x]=y\n                return 0\n            else:\n                return 1\n        def union_b(x,y):\n            x=find_b(x)\n            y=find_b(y)\n            if x!=y:\n                parent_b[x]=y\n                return 0\n            else:\n                return 1\n        count=0\n        for e in edges:\n            if e[0]==3:\n                u1=union_a(e[1],e[2])\n                print(\\\"1\\\")\n                u2=union_b(e[1],e[2])\n                print(\\\"2\\\")\n                if u1==u2 and u1==1:\n                    count+=1\n                    \n        print(parent_a)\n        print(parent_b)\n        \n        for e in edges:\n            if e[0]==1:    \n                u1=union_a(e[1],e[2])\n                print(\\\"3\\\")\n                if u1==1:\n                    count+=1\n            if e[0]==2:    \n                u2=union_b(e[1],e[2])\n                print(\\\"4\\\")\n                if u2==1:\n                    count+=1\n        \n        return count\n                    \n        \n        ", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # Union find\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n\n        res = e1 = e2 = 0\n\n        # Alice and Bob\n        root = [i for i in range(n + 1)]\n        for t, i, j in edges:\n            if t == 3:\n                if uni(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        root0 = root[:]\n\n        # only Alice\n        for t, i, j in edges:\n            if t == 1:\n                if uni(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n\n        # only Bob\n        root = root0\n        for t, i, j in edges:\n            if t == 2:\n                if uni(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if e1 == e2 == n - 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n\n        temp = [[], [], []]\n        for t, a, b in edges:\n            temp[t - 1].append([a, b])\n                \n        p = list(range(n + 1))\n        def find(i):\n            if p[i] != i:\n                p[i] = find(p[i])\n            return p[i]\n        def union(i, j):\n            p[find(i)] = find(j)\n        \n        def helper(c):\n            ans = 0\n            for x, y in c:\n                if find(x) == find(y):\n                    ans += 1\n                else:\n                    union(x, y)\n            return ans\n\n        res = helper(temp[2])\n        old = p[:]\n        for c in temp[:2]:\n            res += helper(c)\n            p = old\n        if sum(x == p[x] for x in range(1, n + 1)) == 1:\n            return res\n        return -1\n        \n        \n\n\n", "class DSU:\n    \n    def __init__(self, N):\n        self.parents = list(range(N))\n        self.ranks = [1] * N\n        self.size = 1\n        \n    def find(self, x):\n        if self.parents[x] != x:\n            self.parents[x] = self.find(self.parents[x])\n        return self.parents[x]\n    \n    def union(self, x, y):\n        px, py = self.find(x), self.find(y)\n        if px == py:\n            return False\n        if self.ranks[px] > self.ranks[py]:\n            self.parents[py] = px\n        elif self.ranks[py] > self.ranks[px]:\n            self.parents[px] = py\n        else:\n            self.parents[px] = py\n            self.ranks[py] += 1\n        self.size += 1\n        return True\n        \n    \nclass Solution:\n    def maxNumEdgesToRemove(self, N: int, edges: List[List[int]]) -> int:\n        uf1, uf2, res = DSU(N), DSU(N), 0\n        \n        for t, u, v in edges:\n            if t == 3:\n                if not uf1.union(u-1, v-1) or not uf2.union(u-1, v-1):\n                    res += 1\n                    \n        for t, u, v in edges:\n            if t == 1 and not uf1.union(u-1, v-1):\n                res += 1\n            elif t == 2 and not uf2.union(u-1, v-1):\n                res += 1\n        \n        return res if uf1.size == N and uf2.size == N else -1", "class DisjointSet:\n    def __init__(self, elements):\n        self.parent = [i for i in range(elements)]\n        self.size = [1] * elements\n    def find(self, value):\n        if self.parent[value] != value:\n            self.parent[value] = self.find(self.parent[value])\n        return self.parent[value]\n    def union(self, value1, value2):\n        parent1, parent2 = self.find(value1), self.find(value2)\n        if parent1 == parent2:\n            return True\n        if self.size[parent1] > self.size[parent2]:\n            self.parent[parent2] = parent1\n            self.size[parent1] += self.size[parent2]\n        else:\n            self.parent[parent1] = parent2\n            self.size[parent2] += self.size[parent1]\n        return False\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        graph = [list() for i in range(n + 1)]\n        for t, u, v in edges:\n            graph[u].append([v, t])\n            graph[v].append([u, t])\n        def dfs(source, ty):\n            nonlocal cnt\n            cnt += 1\n            vis[source] = 1\n            for child, typer in graph[source]:\n                if typer in [ty, 3] and not vis[child]:\n                    dfs(child, ty)\n        cnt = 0\n        vis = [0] * (n + 1)\n        dfs(1, 1)\n        if cnt != n:\n            return -1\n        vis = [0] * (n + 1)\n        cnt = 0\n        dfs(1, 2)\n        if cnt != n:\n            return -1\n        answer = 0\n        dsu1, dsu2 = DisjointSet(n + 1), DisjointSet(n + 1)\n        for t, u, v in edges:\n            if t == 3:\n                dsu1.union(u, v)\n                if dsu2.union(u, v):\n                    answer += 1\n        for t, u, v in edges:\n            if t == 1:\n                if dsu1.union(u, v):\n                    answer += 1\n            if t == 2:\n                if dsu2.union(u, v):\n                    answer += 1\n        return answer\n", "\nclass DSU:\n    \n    def __init__(self, a):\n        self.par = {x:x for x in a}\n    \n    def merge(self, u, v):\n        rootu = self.find(u)\n        rootv = self.find(v)\n        self.par[rootu] = rootv\n    \n    def find(self, u):\n        if self.par[u] != u:\n            self.par[u] = self.find(self.par[u])\n        return self.par[u]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # well its just two graphs, dude\n        \n        # MST would only require n-1 edges\n        # this is ALWAYS TRUE\n        \n        # just process all of the type 3 first\n        \n        # because they will increase group size of both A and B\n        \n        # and then you can just do A and B separately\n        \n        # again, operate on both graphs\n        \n        dsu1 = DSU(range(1,n+1))\n        dsu2 = DSU(range(1,n+1))\n        \n        both = [edge[1:] for edge in edges if edge[0] == 3]\n        alice = [edge[1:] for edge in edges if edge[0] == 1]\n        bob = [edge[1:] for edge in edges if edge[0] == 2]\n        \n        used = 0\n        \n        for u,v in both:\n            if dsu1.find(u) != dsu1.find(v):\n                dsu1.merge(u, v)\n                dsu2.merge(u, v)\n                used += 1\n        \n        for u,v in alice:\n            if dsu1.find(u) != dsu1.find(v):\n                dsu1.merge(u, v)\n                used += 1\n        \n        for u,v in bob:\n            if dsu2.find(u) != dsu2.find(v):\n                dsu2.merge(u, v)\n                used += 1\n        \n        if len(set(dsu1.find(u) for u in dsu1.par)) != 1 or len(set(dsu2.find(u) for u in dsu2.par)) != 1:\n            return -1\n            \n        return len(edges) - used", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(i):\n            while root[i]!=i:\n                root[i]=root[root[i]]\n                i=root[i]\n            return i\n        \n        root=[i for i in range(n+1)]\n        res, cnt1, cnt2=0, 0, 0\n        for t, i, j in edges:\n            if t==3:\n                p1=find(i)\n                p2=find(j)\n                if p1==p2:                    \n                    res+=1\n                else:\n                    root[p1]=p2\n                    cnt1+=1\n                    cnt2+=1\n                    \n        tmp=root[:]\n        for t, i, j in edges:\n            if t==1:\n                p1=find(i)\n                p2=find(j)\n                if p1==p2:                    \n                    res+=1\n                else:\n                    root[p1]=p2\n                    cnt1+=1\n\n        root=tmp[:]\n        for t, i, j in edges:\n            if t==2:\n                p1=find(i)\n                p2=find(j)\n                if p1==p2:\n                    res+=1\n                else:\n                    root[p1]=p2\n                    cnt2+=1\n        \n        return res if cnt1==cnt2==n-1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        alice = DSU()\n        bob = DSU()\n        res = 0\n        for t, u, v in edges:\n            if t == 3:\n                if alice.find(u) == alice.find(v):\n                    res += 1\n                else:\n                    alice.union(u, v)\n                    bob.union(u, v)\n                    \n        for t, u, v in edges:\n            if t == 1:\n                if alice.find(u) == alice.find(v):\n                    res += 1\n                else:\n                    alice.union(u, v)\n            if t == 2:\n                if bob.find(u) == bob.find(v):\n                    res += 1       \n                else:\n                    bob.union(u, v)\n                    \n        if max(bob.count.values()) != n or max(alice.count.values()) != n:\n            return -1\n        \n        return res\n        \nclass DSU:\n    def __init__(self):\n        self.father = {}\n        self.count = {}\n    \n    def find(self, a):\n        self.father.setdefault(a, a)\n        self.count.setdefault(a, 1)\n        if a != self.father[a]:\n            self.father[a] = self.find(self.father[a])\n        return self.father[a]\n    \n    def union(self, a, b):\n        _a = self.find(a)\n        _b = self.find(b)\n        if _a != _b:\n            self.father[_a] = self.father[_b]\n            self.count[_b] += self.count[_a]", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        from collections import defaultdict\n        \n        def collapse(mapping, old_graph):\n            new_graph = defaultdict(set)\n            for node in old_graph:\n                if node not in mapping:\n                    mapping[node] = node\n            \n            duplicate_count = 0\n            for s in old_graph:\n                for e in old_graph[s]:\n                    mapped_s = mapping[s]\n                    mapped_e = mapping[e]\n\n                    if mapped_e in new_graph[mapped_s] or mapped_s == mapped_e:\n                        duplicate_count += 1\n                        # print('collpase_cost', s, 'to', e, 'is mapped to', mapped_s, 'to', mapped_e)\n                        continue\n                    \n                    new_graph[mapped_s].add(mapped_e)\n                    \n            for node in old_graph:\n                if len(new_graph[mapping[node]]) == 0:     \n                    new_graph[mapping[node]] = []\n                \n            return new_graph, duplicate_count//2\n        \n       \n        def find_connected(graph):\n            # return mapping\n            mapping = dict() \n            \n            node_count = 0\n            def dfs(cur_node, parent_node):\n                nonlocal mapping, graph, node_count \n                mapping[cur_node] = parent_node\n                node_count += 1\n                for next_node in graph[cur_node]:\n                    if next_node not in mapping:\n                        dfs(next_node, parent_node)\n                        \n            edges_needed = 0            \n            for node in graph:\n                if node not in mapping:\n                    node_count = 0\n                    dfs(node, node)\n                    edges_needed += node_count-1\n                    \n            return mapping, edges_needed\n        \n\n        a_graph = defaultdict(set)\n        b_graph = defaultdict(set)\n        share_graph = defaultdict(set)\n        point_set = set()\n        for t, s, e in edges:\n            point_set.add(s)\n            point_set.add(e)\n            if t == 1:\n                a_graph[s].add(e)\n                a_graph[e].add(s)\n            elif t == 2:    \n                b_graph[s].add(e)\n                b_graph[e].add(s)\n            else:    \n                share_graph[s].add(e)\n                share_graph[e].add(s)\n        for point in point_set:             \n            if point not in a_graph:\n                a_graph[point] = []\n            if point not in b_graph:\n                b_graph[point] = []\n                \n        costs = []\n        share_mapping, edge_needed = find_connected(share_graph)\n        new_share_graph, share_collapse = collapse(share_mapping, share_graph)\n        costs.append(share_collapse - edge_needed)\n        # print(share_collapse, edge_needed)\n        \n        for graph in [a_graph, b_graph]:\n            # naming is ambiguous here\n            \n            new_graph, share_collpase = collapse(share_mapping, graph)\n            # print(new_graph)\n            sub_mapping, edge_needed = find_connected(new_graph)\n            _, collpased = collapse(sub_mapping, new_graph)\n            costs.append(share_collpase + collpased - edge_needed)\n            # print(collpase, connect_cost, needed)\n            # print(sub_mapping)\n            if len(set(sub_mapping.values())) > 1: return -1\n       \n        return sum(costs)\n", "class Node:\n    def __init__(self, val):\n        self.val = val\n        self.parent = self\n        self.rank = 0\n        self.size = 1\n\nclass DisjointSet:\n    def __init__(self, n):\n        self.sets = {x: Node(x) for x in range(1, n+1)}\n        self.disjointSet = {x: self.sets[x] for x in range(1, n+1)}\n        \n    def findSet(self, x):\n        y = x\n        while y.parent != y:\n            y = y.parent\n        z = x\n        while z != y:\n            tmp = z.parent\n            z.parent = y\n            z = tmp\n        return y\n    \n    def link(self, x_val, y_val):\n        x = self.sets[x_val]\n        y = self.sets[y_val]\n        if x.rank > y.rank:\n            y.parent = x\n            if y_val in self.disjointSet and y_val != x_val:\n                del self.disjointSet[y_val]         \n                x.size += y.size\n        elif x.rank < y.rank:\n            x.parent = y\n            if x_val in self.disjointSet and y_val != x_val:\n                del self.disjointSet[x_val]\n                y.size += x.size\n        else:\n            x.parent = y\n            y.rank += 1\n            if x_val in self.disjointSet and y_val != x_val:\n                del self.disjointSet[x_val]\n                y.size += x.size\n        \n    def union(self, x, y):\n        self.link(self.findSet(self.sets[x]).val, self.findSet(self.sets[y]).val)\n         \n\nclass Solution:\n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        alice = DisjointSet(n)\n        bob = DisjointSet(n)\n        both = DisjointSet(n)\n        type1 = 0\n        type2 = 0\n        type3 = 0\n        for edge in edges:\n            etype, a, b = edge\n            if etype == 1:\n                alice.union(a, b)\n                type1 += 1\n            elif etype == 2:\n                bob.union(a, b)\n                type2 += 1\n            else:\n                alice.union(a, b)\n                bob.union(a, b)\n                both.union(a, b)\n                type3 += 1\n        # print(alice.disjointSet)\n        # print(bob.disjointSet)\n        # print(both.disjointSet)\n        # print(len(bob.disjointSet))\n        \n        if len(alice.disjointSet) != 1 or len(bob.disjointSet) != 1:\n            return -1\n        tmp = 0\n        for key, val in list(both.disjointSet.items()):\n            tmp += val.size - 1\n        return type3 - tmp + type1 - (len(both.disjointSet) - 1) + type2 - (len(both.disjointSet) - 1)\n        \n            \n        \n\n                \n", "from collections import defaultdict\nclass UnionFind:\n    def __init__(self, iterable=None):\n        \\\"\\\"\\\"\u521d\u59cb\u5316\u7236\u5b50\u5173\u7cfb\u6620\u5c04\u3002\u82e5\u6307\u5b9aiterable\uff0c\u5219\u521d\u59cb\u5316\u5176\u81ea\u8eab\\\"\\\"\\\"\n        self.cnt = defaultdict(lambda: 1)\n        self.f = {}\n        for a in iterable or []:\n            self.f[a] = a\n            self.cnt[a] = 1\n\n    def size(self, a=None):\n        \\\"\\\"\\\"\u8fd4\u56dea\u96c6\u5408\u5927\u5c0f\u3002\u82e5\u4e0d\u6307\u5b9aa\uff0c\u5219\u8fd4\u56de\u96c6\u5408\u7684\u4e2a\u6570\\\"\\\"\\\"\n        if a is not None:\n            return self.cnt[self.find(a)]\n        else:\n            return sum(a == self.f[a] for a in self.f)\n\n    def same(self, a, b):\n        \\\"\\\"\\\"\u5224\u65ada,b\u662f\u5426\u540c\u4e00\u96c6\u5408\\\"\\\"\\\"\n        return self.find(a) == self.find(b)\n\n    def find(self, a):\n        \\\"\\\"\\\"\u67e5\u627ea\u7684\u6839\\\"\\\"\\\"\n        if self.f.get(a, a) == a:\n            self.f[a] = a\n            return a\n        self.f[a] = self.find(self.f[a])\n        return self.f[a]\n\n    def merge(self, a, b):\n        \\\"\\\"\\\"\u5408\u5e76a\u5230b\u7684\u96c6\u5408\\\"\\\"\\\"\n        ra = self.find(a)\n        rb = self.find(b)\n        if ra != rb:\n            self.f[ra] = rb\n            self.cnt[rb] += self.cnt[ra]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        un = UnionFind(range(1, n+1))\n        ans = 0\n        for t, a, b in edges:\n            if t == 3:\n                if un.same(a, b):\n                    ans += 1\n                else:\n                    un.merge(a, b)\n\n        un1 = deepcopy(un)\n        for t, a, b in edges:\n            if t == 1:\n                if un1.same(a, b):\n                    ans += 1\n                else:\n                    un1.merge(a, b)\n\n        un2 = deepcopy(un)\n        for t, a, b in edges:\n            if t == 2:\n                if un2.same(a, b):\n                    ans += 1\n                else:\n                    un2.merge(a, b)\n        \n        return ans if un1.size() == un2.size() == 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        root = [i for i in range(n+1)]\n        def find(x):\n            if root[x] != x:\n                root[x] = find(root[x])\n            return root[x]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n\n        ans = e1 = e2 = 0\n        for t, i, j in edges:\n            if t == 3:\n                if uni(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    ans += 1\n\n        root_origin = root\n        root = root_origin[:]\n        for t, i , j in edges:\n            if t == 1:\n                if uni(i, j):\n                    e1 += 1\n                else:\n                    ans += 1\n        \n        root = root_origin[:]\n        for t, i, j in edges:\n            if t == 2:\n                if uni(i, j):\n                    e2 += 1\n                else:\n                    ans += 1\n\n        if e1 == n -1 and e2 == n -1:\n            return ans\n        else:\n            return -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # Union find\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n\n        res = e1 = e2 = 0\n\n        # Alice and Bob\n        root = list(range(n + 1))\n        for t, i, j in edges:\n            if t == 3:\n                if uni(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        root0 = root[:]\n\n        # only Alice\n        for t, i, j in edges:\n            if t == 1:\n                if uni(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n\n        # only Bob\n        root = root0\n        for t, i, j in edges:\n            if t == 2:\n                if uni(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if e1 == e2 == n - 1 else -1\n\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, e: List[List[int]]) -> int:\n        pa=[0]*(n+1)\n        pb=[0]*(n+1)\n        for i in range(n+1):\n            pa[i]=i\n            pb[i]=i\n        \n        def p(x):\n            if x==pa[x]:\n                return x\n            pa[x]=p(pa[x])\n            return pa[x]\n        \n        def pp(x):\n            if x==pb[x]:\n                return x\n            pb[x]=pp(pb[x])\n            return pb[x]\n        ans=0\n        for x in e:\n            if x[0]==3:\n                q=p(x[1])\n                w=p(x[2])\n                \n                r=pp(x[1])\n                t=pp(x[2])\n                \n                if (q==w and r==t):\n                    ans+=1\n                else:\n                    pa[q]=w\n                    pb[r]=t\n                    \n        for x in e:\n            \n            if x[0]==1:\n                q=p(x[1])\n                w=p(x[2])\n                \n                \n                \n                if (q==w ):\n                    ans+=1\n                else:\n                    pa[q]=w\n        \n            if x[0]==2:\n                r=pp(x[1])\n                t=pp(x[2])\n        \n                if (r==t):\n                    ans+=1\n                else:\n                    pb[r]=t\n        tt=0\n        for i in range(1,n+1):\n            if i==pa[i]:\n                tt+=1\n            if i==pb[i]:\n                tt+=1\n        \n        if tt!=2:\n            return -1\n        return ans\n                \n            \n        \n        \n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        roots1 = [i for i in range(n + 1)]\n        roots2 = [i for i in range(n + 1)]\n        \n        def find_root(roots, i):\n            while roots[i] != i:\n                roots[i] = roots[roots[i]]\n                i = roots[i]\n            return i\n        \n        ans = 0\n        \n        edges.sort(reverse=True)\n        \n        for edge in edges:\n            if edge[0] == 3:\n                if find_root(roots1, edge[1]) == find_root(roots1, edge[2]) and find_root(roots2, edge[1]) == find_root(roots2, edge[2]):\n                    ans += 1\n                else:\n                    roots1[find_root(roots1, edge[2])] = find_root(roots1, edge[1])\n                    roots2[find_root(roots2, edge[2])] = find_root(roots2, edge[1])\n            elif edge[0] == 1:\n                if find_root(roots1, edge[1]) == find_root(roots1, edge[2]):\n                    ans += 1\n                else:\n                    roots1[find_root(roots1, edge[2])] = find_root(roots1, edge[1])\n            else:\n                if find_root(roots2, edge[1]) == find_root(roots2, edge[2]):\n                    ans += 1\n                else:\n                    roots2[find_root(roots2, edge[2])] = find_root(roots2, edge[1])\n        \n        def check(roots):\n            g = set()\n            for i in range(1, len(roots)):\n                g.add(find_root(roots, i))\n            return 0 if len(g) > 1 else 1\n        \n        return -1 if not check(roots1) or not check(roots2) else ans\n                    \n        \n        \n", "import copy\n\ndef union(subsets, u, v):\n    uroot = find(subsets, u)\n    vroot = find(subsets, v)\n    \n    if subsets[uroot][1] > subsets[vroot][1]:\n        subsets[vroot][0] = uroot\n    if subsets[vroot][1] > subsets[uroot][1]:\n        subsets[uroot][0] = vroot\n    if subsets[uroot][1] == subsets[vroot][1]:\n        subsets[vroot][0] = uroot\n        subsets[uroot][1] += 1\n    \n\ndef find(subsets, u):\n    if subsets[u][0] != u:\n        subsets[u][0] = find(subsets, subsets[u][0])\n    return subsets[u][0]\n\n\nclass Solution:\n    #kruskal's\n    #1 is alice and 2 is bob\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        subsets1 = ['1 index'] + [[x+1,0] for x in range(n)] #Alice's unionfind\n        subsets2 = ['1 index'] + [[x+1,0] for x in range(n)] #Bob's unionfind\n        \n        #edges = sorted(edges, key= lambda e: -e[0])\n        e = 0 #number of total edges used\n        e1 = 0 #number of edges for Alice\n        e2 = 0 #number of edges for Bob\n        i = 0 #track position in edges list\n        \n        #start with type 3 edges\n        while e < n - 1:\n            if i == len(edges): \n                i = 0\n                break\n            typ, u, v = edges[i]\n            if typ != 3: \n                i += 1\n                continue\n            if find(subsets1, u) != find(subsets1, v):\n                union(subsets1, u, v)\n                e += 1\n            \n            i += 1\n        \n        #everything that was done to Alice applies to Bob\n        e1 = e\n        e2 = e\n        subsets2 = copy.deepcopy(subsets1)\n        i=0\n        #once done with shared edges, do Bob's\n        while e2 < n-1:\n            if i == len(edges): \n                i = 0\n                break\n            typ, u, v = edges[i]\n            if typ != 2: \n                i+=1\n                continue\n            if find(subsets2, u) != find(subsets2, v):\n                union(subsets2, u, v)\n                e += 1\n                e2 += 1\n            i += 1\n        \n        if e2 < n - 1: \n            return -1 #if we've used all edges bob can use (types 2 and 3) and he still can't reach all nodes, ur fucked\n        \n        i=0\n        #now finish Alice's MST\n        while e1 < n-1:\n            if i == len(edges): \n                return -1\n        \n            typ, u, v = edges[i]\n            if typ != 1: \n                i += 1\n                continue\n            \n            if find(subsets1, u) != find(subsets1, v):\n                union(subsets1, u, v)\n                e += 1\n                e1 += 1\n            i += 1\n            \n        return len(edges) - e\n            \n            \n            \n            \n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        t1, t2, t3 = [], [], []\n        for t,s,e in edges:\n            if t==1: t1.append([s,e])\n            elif t==2: t2.append([s,e])\n            elif t==3: t3.append([s,e])\n        # print(t1,t2,t3)\n        \n        def find(x, arr):\n            if x==arr[x]: return x\n            else: \n                arr[x] = find(arr[x], arr)\n                return arr[x]\n                \n        ans = 0\n        alice, bob = [i for i in range(n+1)], [i for i in range(n+1)]\n        \n        for s,e in t3:\n            g1, g2 = find(s,alice), find(e,alice)\n            if g1!=g2:\n                alice[g1] = g2\n                bob[g1] = g2\n            else: ans += 1\n        # print(alice, bob)\n        for s,e in t1:\n            g1, g2 = find(s,alice), find(e,alice)\n            if g1!=g2:\n                alice[g1] = g2\n            else: ans += 1\n        # print(alice, bob, ans)\n        for s,e in t2:\n            g1, g2 = find(s,bob), find(e,bob)\n            if g1!=g2:\n                bob[g1] = g2\n            else: ans += 1\n        \n        root1 = find(alice[1], alice)\n        for i in range(1, len(alice)):\n            if find(i, alice)!=root1: return -1\n        root2 = find(bob[1], bob)\n        for i in range(1, len(bob)):\n            if find(i, bob)!=root2: return -1\n        return ans\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        A=[i+1 for i in range(n)]\n        B=[i+1 for i in range(n)]\n        cA=n\n        cB=n\n        edges.sort(reverse=True,key=lambda x:(x[0]))\n        i=0\n        m=len(edges)\n        ans=0\n        while i<m and edges[i][0]==3:\n            l1=find(A,edges[i][1])\n            l2=find(A,edges[i][2])\n            if l1==l2:\n                ans+=1\n            else:\n                #mi=min(l1,l2)\n                #ma=max(l1,l2)\n                A[l1-1]=-l2\n                #print(l1,l2)\n                #print(A)\n                B[l1-1]=-l2\n                cA-=1\n                cB-=1\n            if cA==1:\n                return m-i-1+ans\n            i+=1\n        j=i\n        while j<m and edges[j][0]==2:\n            l1=find(B,edges[j][1])\n            l2=find(B,edges[j][2])\n            if l1==l2:\n                ans+=1\n            else:\n                #mi=min(l1,l2)\n                #ma=max(l1,l2)\n                B[l1-1]=-l2\n                cB-=1\n            if cB==1:\n                while j+1<m and edges[j+1][0]==2:\n                    j+=1\n                    ans+=1\n                j+=1\n                break\n            else:\n                j+=1\n        if cB!=1:\n            return -1\n        while j<m:\n            l1=find(A,edges[j][1])\n            l2=find(A,edges[j][2])\n            if l1==l2:\n                ans+=1\n            else:\n                #mi=min(l1,l2)\n                #ma=max(l1,l2)\n                A[l1-1]=-l2\n                cA-=1\n            if cA==1:\n                return m-j-1+ans\n            j+=1\n        \n        return -1\n        \n            \n            \n            \ndef find(ll,x):\n    if ll[x-1]>0:\n        return ll[x-1]\n    else:\n        ll[x-1]=-find(ll,-ll[x-1])\n        return(-ll[x-1])\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        graphs = [collections.defaultdict(list) for _ in range(3)]\n        x = [0]*3\n        for c, i, j in edges:\n            graphs[c-1][i].append((-c, j))\n            graphs[c-1][j].append((-c, i))\n            x[c-1] += 1\n        e = graphs[2][1] + graphs[0][1]\n        heapq.heapify(e)\n        treeset = set([1])\n        ans = 0\n        while e:\n            c, y = heapq.heappop(e)\n            if y not in treeset:\n                treeset.add(y)\n                if c == -3:\n                    ans += 1\n                for item in graphs[2][y]:\n                    heapq.heappush(e, item)\n                for item in graphs[0][y]:\n                    heapq.heappush(e, item)\n        # print(treeset)\n        # print(ans)\n        if len(treeset) != n:\n            return -1\n        e = graphs[2][1] + graphs[1][1]\n        heapq.heapify(e)\n        treeset = set([1])\n        # ans = 0\n        # print(e)\n        while e:\n            c, y = heapq.heappop(e)\n            if y not in treeset:\n                treeset.add(y)\n                # if c == -3:\n                #     ans += 1\n                for item in graphs[2][y]:\n                    heapq.heappush(e, item)\n                for item in graphs[1][y]:\n                    heapq.heappush(e, item)\n            # print(e)\n        # print(treeset)\n        if len(treeset) != n:\n            return -1\n        # print(ans)\n        return len(edges)+ans -2*(n-1)\n", "\nclass Solution(object):\n    def maxNumEdgesToRemove(self, n, edges):\n        \\\"\\\"\\\"\n        :type n: int\n        :type edges: List[List[int]]\n        :rtype: int\n        \\\"\\\"\\\"\n\n        ga = [i for i in range(n)]\n        gb = [i for i in range(n)]\n\n        res = 0\n\n        e1 = []\n        e2 = []\n        for i in range(len(edges)):\n            if edges[i][0] == 1:\n                e1.append(edges[i])\n                continue\n            if edges[i][0] == 2:\n                e2.append(edges[i])\n                continue\n\n            x = edges[i][1]-1\n            y = edges[i][2]-1\n\n            gx = self.get_group(ga, x)\n            gy = self.get_group(ga, y)\n            if gx == gy:\n                res += 1\n            ga[gx] = min(gx, gy)\n            ga[gy] = min(gx, gy)\n            ga[x] = min(gx, gy)\n            ga[y] = min(gx, gy)\n            gb[gx] = min(gx, gy)\n            gb[gy] = min(gx, gy)\n            gb[x] = min(gx, gy)\n            gb[y] = min(gx, gy)\n\n        for e in e1:\n            x = e[1]-1\n            y = e[2]-1\n            gx = self.get_group(ga, x)\n            gy = self.get_group(ga, y)\n            if gx == gy:\n                res += 1\n            ga[gx] = min(gx, gy)\n            ga[gy] = min(gx, gy)\n            ga[x] = min(gx, gy)\n            ga[y] = min(gx, gy)\n\n        for e in e2:\n            x = e[1]-1\n            y = e[2]-1\n            gx = self.get_group(gb, x)\n            gy = self.get_group(gb, y)\n            if gx == gy:\n                res += 1\n            gb[gx] = min(gx, gy)\n            gb[gy] = min(gx, gy)\n            gb[x] = min(gx, gy)\n            gb[y] = min(gx, gy)\n\n        ga0 = self.get_group(ga, 0)\n        gb0 = self.get_group(gb, 0)\n        for i in range(1, n):\n            gai = self.get_group(ga, i)\n            gbi = self.get_group(gb, i)\n            if ga0 != gai or gb0 != gbi:\n                return -1\n        return res\n\n    def get_group(self, g, i):\n\n        gi = g[i]\n        pre = i\n        while gi != g[gi]:\n            g[pre] = g[gi]\n            pre = gi\n            gi = g[gi]\n\n        return gi", "class DSU:\n    def __init__(self, N):\n        self.par = list(range(N))\n        self.sz = [1] * N\n\n    def find(self, x):\n        if self.par[x] != x:\n            self.par[x] = self.find(self.par[x])\n        return self.par[x]\n\n    def union(self, x, y):\n        xr, yr = self.find(x), self.find(y)\n        if xr == yr:\n            return False\n        if self.sz[xr] < self.sz[yr]:\n            xr, yr = yr, xr\n        self.par[yr] = xr\n        self.sz[xr] += self.sz[yr]\n        return True\n    \n    def size(self, x):\n        return self.sz[self.find(x)]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, a: List[List[int]]) -> int:\n        dsu1, dsu2 = DSU(n), DSU(n)\n        d = {}\n        \n        for t, x, y in a:\n            x -= 1\n            y -= 1\n            if t in d:\n                d[t].append([x, y])\n            else:\n                d[t] = [[x,y]]\n                \n        ans = 0\n        \n        if 3 in d:\n            for i in d[3]:\n                x, y = i\n                dsu2.union(x, y)\n                if not dsu1.union(x, y):\n                    ans += 1\n        \n        if 1 in d:\n            for i in d[1]:\n                x, y = i\n                if not dsu1.union(x, y):\n                    ans += 1\n                \n        if 2 in d:\n            for i in d[2]:\n                x, y = i\n                if not dsu2.union(x, y):\n                    ans += 1\n        \n        if dsu1.size(0) != n or dsu2.size(0) != n:\n            return -1\n        return ans", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        type_1, type_2, type_3 = [], [], []\n        for type, a, b in edges:\n            if type == 3:\n                type_3.append((a, b))\n            elif type == 1:\n                type_1.append((a,b))\n            elif type == 2:\n                type_2.append((a, b))\n        answer = 0\n        b_u = None\n        def helper(edges, u=None):\n            nonlocal b_u\n            u = Union(n, u)\n            nonlocal answer\n            for a, b in edges:\n                if not u.union(a, b):\n                    answer += 1\n            b_u = u.backup()\n            if not u.isConnected():\n                return False\n            return True\n        helper(type_3)\n        bb_u = copy.deepcopy(b_u)\n        if not helper(type_1, copy.deepcopy(bb_u)):\n            return -1\n        if not helper(type_2, copy.deepcopy(bb_u)):\n            return -1\n        return answer\n\n\nclass Union:\n    def __init__(self, n, p=None):\n        self.n = n\n        self.p = p if p else {i: i for i in range(1, n+1)}\n\n    def backup(self):\n        return self.p\n\n    def union(self, p_a, b):\n        a = p_a\n        while self.p[a] != a:\n            a = self.p[a]\n        while self.p[b] != b:\n            b = self.p[b]\n        self.p[p_a] = a\n        if a == b:\n            return False\n        else:\n            self.p[b] = a\n            return True\n\n    def isConnected(self):\n        return sum(i == self.p[i] for i in range(1, self.n+1)) == 1", "class unionfindset:\n    def __init__(self,n=0):\n        self.par={}\n        self.rank={}\n        self.count=n\n        for i in range(1,1+n):\n            self.par[i]=i\n            self.rank[i]=1\n        \n    def find(self,u):\n        \n        if u!=self.par[u]:\n            self.par[u]=self.find(self.par[u])\n        return self.par[u]\n    \n    def union(self,u,v):\n        pu,pv=self.find(u),self.find(v)\n        if pu==pv:return False\n        if self.rank[pu]<self.rank[pv]:\n            self.par[pu]=pv\n        elif self.rank[pv]<self.rank[pu]:\n            self.par[pv]=pu\n        else:\n            self.par[pv]=pu\n            self.rank[pu]+=1\n        self.count-=1\n        return True\n            \n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        \n        unf1=unionfindset(n)\n        unf2=unionfindset(n)\n        out=0\n        for i,u,v in edges:\n            if i==1 or i==2:continue\n            if not unf1.union(u,v) or not unf2.union(u,v):out+=1\n            \n            \n            \n        \n        for i,u,v in edges:\n            if i==1:\n                #print(u,v)\n                if not unf1.union(u,v):out+=1\n                \n            elif i==2:\n                #print(u,v)\n                if not unf2.union(u,v):out+=1\n                \n       \n        if unf1.count!=1 or unf2.count!=1:return -1\n        \n        return out\n\n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # Union find\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: \n                return False\n            root[x] = y\n            return True\n\n        res = e1 = e2 = 0\n        \n        \n        root = list(range(n+1))\n        \n        print(root)\n\n        \n        for typ, u, v in edges:\n            if typ == 3:\n                if uni(u, v):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n                    \n        root_copy = root.copy()\n        \n        for typ, u, v in edges:\n            if typ == 1:\n                if uni(u, v):\n                    e1 += 1\n                else:\n                    res += 1\n\n        root = root_copy\n        \n        for typ, u, v in edges:\n            if typ == 2:\n                if uni(u, v):\n                    e2 += 1\n                else:\n                    res += 1\n                    \n        return res if e1 == e2 == n - 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        roots1 = [i for i in range(n + 1)]\n        roots2 = [i for i in range(n + 1)]\n        \n        def find_root(roots, i):\n            while roots[i] != i:\n                roots[i] = roots[roots[i]]\n                i = roots[i]\n            return i\n        \n        ans = 0\n        \n        edges.sort(reverse=True)\n        \n        for edge in edges:\n            if edge[0] == 3:\n                if find_root(roots1, edge[1]) == find_root(roots1, edge[2]) and find_root(roots2, edge[1]) == find_root(roots2, edge[2]):\n                    ans += 1\n                else:\n                    roots1[find_root(roots1, edge[2])] = find_root(roots1, edge[1])\n                    roots2[find_root(roots2, edge[2])] = find_root(roots2, edge[1])\n            elif edge[0] == 1:\n                if find_root(roots1, edge[1]) == find_root(roots1, edge[2]):\n                    ans += 1\n                else:\n                    roots1[find_root(roots1, edge[2])] = find_root(roots1, edge[1])\n            else:\n                if find_root(roots2, edge[1]) == find_root(roots2, edge[2]):\n                    ans += 1\n                else:\n                    roots2[find_root(roots2, edge[2])] = find_root(roots2, edge[1])\n        \n        g1 = set()\n        for i in range(1, len(roots1)):\n            g1.add(find_root(roots1, i))\n        if len(g1) > 1:\n            return -1\n        \n        g2 = set()\n        for i in range(1, len(roots2)):\n            g2.add(find_root(roots2, i))\n        if len(g2) > 1:\n            return -1\n        \n        return ans\n                    \n        \n        \n", "class Solution:\n    def num_components(self, n, edges):\n        parents = [0 for _ in range(n+1)]\n        components = n\n        def root(u):\n            if parents[u] == 0:\n                return u\n            r = root(parents[u])\n            parents[u] = r\n            return r\n        for u, v in edges:\n            a, b = root(u), root(v)\n            if a == b:\n                continue\n            else:\n                components -= 1\n                parents[a] = b\n        return components\n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        A = [edge[1:] for edge in edges if edge[0] == 1]\n        B = [edge[1:] for edge in edges if edge[0] == 2]\n        C = [edge[1:] for edge in edges if edge[0] == 3]\n        a = self.num_components(n, A + C)\n        b = self.num_components(n, B + C)\n        c = self.num_components(n, C)\n        #print(f'a={a}, b={b}, c={c}')\n        if a > 1 or b > 1:\n            return -1\n        deleted_common_edges = len(C) - n + c\n        deleted_alice_edges = len(A) + len(C) - n + 1 - deleted_common_edges\n        deleted_bob_edges = len(B) + len(C) - n + 1 - deleted_common_edges\n        #print(deleted_common_edges, deleted_alice_edges, deleted_bob_edges)\n        return deleted_common_edges + deleted_alice_edges + deleted_bob_edges", "class Solution:\n    def maxNumEdgesToRemove(self, N: int, E: List[List[int]], same = 0) -> int:\n        E = [[_, u - 1, v - 1] for _, u, v in E]\n        A = [i for i in range(N)]\n        B = [i for i in range(N)]\n        # def find(P, x): P[x] = x if P[x] == x else find(P, P[x])\n        def find(P, x): P[x] = P[x] if P[x] == x else find(P, P[x]); return P[x]\n        def union(P, a, b):\n            a = find(P, a)\n            b = find(P, b)\n            if a == b:\n                return 1\n            P[a] = b  # arbitrary choice\n            return 0\n        for type, u, v in E:\n            if type == 3: same += union(A, u, v) | union(B, u, v)\n        for type, u, v in E:\n            if type == 1: same += union(A, u, v)\n            if type == 2: same += union(B, u, v)\n        parentA = find(A, 0)\n        parentB = find(B, 0)\n        return same if all(parentA == find(A, x) for x in A) and all(parentB == find(B, x) for x in B) else -1\n        \n", "class UnionFind():\n    def __init__(self, n):\n        self.label = list(range(n))\n        self.sz = [1] * n\n        \n    def find(self, p):\n        while p != self.label[p]:\n            self.label[p] = self.label[self.label[p]]\n            p = self.label[p]\n        return p\n    \n    def union(self, p, q):\n        proot, qroot = self.find(p), self.find(q)\n        if self.sz[proot] >= self.sz[qroot]:\n            self.label[qroot] = proot\n            self.sz[proot] += self.sz[qroot]\n        else:\n            self.label[proot] = qroot\n            self.sz[qroot] += self.sz[proot]\n    \n    def size(self, p):\n        return self.sz[self.find(p)]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \\\"\\\"\\\"\n        MST, union find?\n        \n        construct uf based on type 3\n        try type 1 and type2 separate\n        for a type:\n          pick from all edges to achieve reachability\n        \\\"\\\"\\\"\n        \n        def mst(uf, edges):\n            result = 0\n            for a, b in edges:\n                if uf.find(a - 1) == uf.find(b - 1):\n                    continue\n                uf.union(a - 1, b - 1)\n                result += 1\n            if uf.size(0) != n:\n                return -1\n            return result\n        \n        def commonUf(edges):\n            uf = UnionFind(n)\n            edgesNeeded = 0\n            for a, b in edges:\n                if uf.find(a - 1) == uf.find(b - 1):\n                    continue\n                uf.union(a - 1, b - 1)\n                edgesNeeded += 1\n            return uf, edgesNeeded\n        \n        commonEdges = [(edge[1], edge[2]) for edge in edges if edge[0] == 3]\n        \n        uf1, commonEdgesNeeded = commonUf(commonEdges)\n        mst1 = mst(uf1, [(edge[1], edge[2]) for edge in edges if edge[0] == 1])\n        uf2, _ = commonUf(commonEdges)\n        mst2 = mst(uf2, [(edge[1], edge[2]) for edge in edges if edge[0] == 2])\n        \n        if mst1 == -1 or mst2 == -1:\n            return -1\n        return len(edges) - commonEdgesNeeded - mst1 - mst2\n        ", "class UnionFind:\n    def __init__(self, ):\n        self._parent = {}\n        self._size = {}\n    \n    def union(self, a, b):\n        a = self.find(a)\n        b = self.find(b)\n        if a == b:\n            return False\n        if self._size[a] < self._size[b]:\n            a, b = b, a\n        self._parent[b] = a\n        self._size[a] += self._size[b]\n        return True\n    \n    def find(self, x):\n        if x not in self._parent:\n            self._parent[x] = x\n            self._size[x] = 1\n            return x\n        while self._parent[x] != x:\n            self._parent[x] = self._parent[self._parent[x]]\n            x = self._parent[x]\n        return self._parent[x]\n    \n    def size(self, x):\n        return self._size[self.find(x)]\n        \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        ufa = UnionFind()\n        ufb = UnionFind()\n        ufa2 = UnionFind()\n        ufb2 = UnionFind()\n        count = 0\n        \n        for t, u, v in edges:\n            if t == 1:\n                ufa.union(u, v)\n            elif t == 2:\n                ufb.union(u, v)\n            else:\n                ufa.union(u, v)\n                ufb.union(u, v)\n                ufa2.union(u, v)\n                count += int(ufb2.union(u, v))\n        \n        if ufa.size(1) != n or ufb.size(1) != n:\n            return -1\n        \n        for t, u, v in edges:\n            if t == 1:\n                count += ufa2.union(u, v)\n            elif t == 2:\n                count += ufb2.union(u, v)\n        \n        return len(edges) - count", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1 = list(range(n+1))\n        uf2 = list(range(n+1))\n        uf3 = list(range(n+1))\n        \n        def find(uf, x):\n            if x != uf[x]:\n                uf[x] = find(uf, uf[x])\n            return uf[x]\n        \n        def union(uf, x, y):\n            uf[find(uf, x)] = find(uf, y)\n            \n        res = 0\n        for t, u, v in edges:\n            if t == 3 and find(uf3, u) != find(uf3, v):\n                union(uf1, u, v)\n                union(uf2, u, v)\n                union(uf3, u, v)\n                res += 1\n                \n        for t, u, v in edges:\n            if t == 1 and find(uf1, u) != find(uf1, v):\n                union(uf1, u, v)\n                res += 1\n            if t == 2 and find(uf2, u) != find(uf2, v):\n                union(uf2, u, v)\n                res += 1\n                    \n        if len({find(uf1, i) for i in range(1,n+1)}) > 1:\n            return -1\n        if len({find(uf2, i) for i in range(1,n+1)}) > 1:\n            return -1\n                    \n        return len(edges) - res", "\\\"\\\"\\\"\nN connected nodes, M edges (M <= N*(N-1)//2)\nwhat is the minimum number of edges to connect N nodes?\n\n\n\n\\\"\\\"\\\"\n\n\n\nclass UnionFindSet:\n    def __init__(self, n):\n        self.parents = list(range(n))\n        self.ranks = [1] * n\n        self.size = 1\n        \n    def find(self, u):\n        if u != self.parents[u]:\n            self.parents[u] = self.find(self.parents[u])\n        return self.parents[u]\n    \n    def union(self, u, v):\n        pu, pv = self.find(u), self.find(v)\n        if pu == pv:\n            return False\n        if self.ranks[pu] > self.ranks[pv]:\n            self.parents[pv] = pu\n        elif self.ranks[pv] > self.ranks[pu]:\n            self.parents[pu] = pv\n        else:\n            self.parents[pu] = pv\n            self.ranks[pv] += 1\n        self.size += 1\n        return True\n    \n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1 = UnionFindSet(n)\n        uf2 = UnionFindSet(n)\n        res = 0\n        \n        for t, u, v in edges:\n            if t != 3:\n                continue\n            if not uf1.union(u - 1, v - 1) or not uf2.union(u - 1, v - 1):\n                res += 1\n        \n        for t, u, v in edges:\n            if t == 1 and not uf1.union(u - 1, v - 1):\n                res += 1\n            elif t == 2 and not uf2.union(u - 1, v - 1):\n                res += 1\n   \n        return res if uf1.size == n and uf2.size == n else -1\n\n\n\n\n        \n        \n        \n        \n        \n        ", "class UnionFind:\n    def __init__(self, n):\n        self.state = [-1] * n\n        # self.size_table = [1] * n\n        # cnt\u306f\u30b0\u30eb\u30fc\u30d7\u6570\n        self.cnt = n\n\n    def root(self, u):\n        v = self.state[u]\n        if v < 0: return u\n        self.state[u] = res = self.root(v)\n        return res\n\n    def merge(self, u, v):\n        ru = self.root(u)\n        rv = self.root(v)\n        if ru == rv: return\n        du = self.state[ru]\n        dv = self.state[rv]\n        if du > dv: ru, rv = rv, ru\n        if du == dv: self.state[ru] -= 1\n        self.state[rv] = ru\n        self.cnt -= 1\n        # self.size_table[ru] += self.size_table[rv]\n        return\n\n    # \u30b0\u30eb\u30fc\u30d7\u306e\u8981\u7d20\u6570\n    # def size(self, u):\n    #     return self.size_table[self.root(u)]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        au=UnionFind(n)\n        bu=UnionFind(n)\n        ee=[[] for _ in range(3)]\n        for t,u,v in edges:\n            ee[t-1].append((u-1,v-1))\n\n        ans=0\n\n        for u,v in ee[2]:\n            if au.root(u)==au.root(v):\n                ans+=1\n                continue\n            au.merge(u,v)\n            bu.merge(u,v)\n\n        for u,v in ee[0]:\n            if au.root(u)==au.root(v):\n                ans+=1\n                continue\n            au.merge(u,v)\n\n        for u,v in ee[1]:\n            if bu.root(u)==bu.root(v):\n                ans+=1\n                continue\n            bu.merge(u,v)\n\n        if au.cnt==1 and bu.cnt==1:return ans\n        else:return -1\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        parent = list(range(n + 1))\n        def find(x):\n            if parent[x] != x:\n                parent[x] = find(parent[x]) \n            return parent[x]\n        \n        def union(x , y):\n            rx, ry = find(x), find(y)\n            if rx != ry:\n                parent[rx] = ry\n                return 1\n            else:\n                return 0\n        \n        res, e1, e2 = 0, 0, 0\n        \n        for t, i, j in edges:\n            if t == 3:\n                if union(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n                    \n        parent1 = parent[:]\n        \n        for t, i ,j in edges:\n            if t == 1:\n                if union(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n        \n        parent = parent1\n        for t, i, j in edges:\n            if t == 2:\n                if union(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n        return res if e1 == e2 == n - 1 else -1\n        \n", "from typing import List\nclass Solution:\n    def maxNumEdgesToRemove(self, n, edges):\n        # Union find\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n\n        res = e1 = e2 = 0\n\n        # Alice and Bob\n        root = list(range(n + 1))\n        for t, i, j in edges:\n            if t == 3:\n                if uni(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        root0 = root[:]\n\n        # only Alice\n        for t, i, j in edges:\n            if t == 1:\n                if uni(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n\n        # only Bob\n        root = root0\n        for t, i, j in edges:\n            if t == 2:\n                if uni(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if e1 == e2 == n - 1 else -1", "import copy\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        parA, parB = [i for i in range(n + 1)], [i for i in range(n + 1)]\n        cnta, cntb = n, n\n        edges3 = [edge for edge in edges if edge[0] == 3]\n        edges2 = [edge for edge in edges if edge[0] == 2]\n        edges1 = [edge for edge in edges if edge[0] == 1]\n        \n        def find(par, x):\n            p = par[x]\n            while p != par[p]:\n                p = par[p]\n            par[x] = p\n            return p\n\n        ans = 0\n        \n        for e in edges3:\n            x, y = find(parA, e[1]), find(parA, e[2])\n            if x != y:\n                parA[y] = x\n                parB[y] = x\n                cnta -= 1\n                cntb -= 1\n            else:\n                ans += 1\n        \n        for e in edges1:\n            x, y = find(parA, e[1]), find(parA, e[2])\n            if x != y:\n                parA[y] = x\n                cnta -= 1\n            else:\n                ans += 1\n                \n        for e in edges2:\n            x, y = find(parB, e[1]), find(parB, e[2])\n            if x != y:\n                parB[y] = parB[x]\n                cntb -= 1\n            else:\n                ans += 1\n        if cnta != 1 or cntb != 1:\n            return -1\n        \n        return ans\n\n    \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        types = [[] for _ in range(3)]\n        for t, *edge in edges:\n            types[t - 1].append(edge)\n        \n        removed = 0\n        self.parent = list(range(n + 1))\n        self.size = [1] * (n + 1)\n        \n        # union type 3 first\n        for u, v in types[2]:\n            if self.isConnected(u, v):\n                removed += 1\n                continue\n            self.union(u, v)\n        \n        self.parent_copy = list(self.parent)\n        self.size_copy = list(self.size)\n        \n        for u, v in types[0]:\n            if self.isConnected(u, v):\n                removed += 1\n                continue\n            self.union(u, v)\n        \n        if sum([i == self.parent[i] for i in range(1, n + 1)]) > 1:\n            return -1\n        \n        self.parent = self.parent_copy\n        self.size = self.size_copy\n        \n        for u, v in types[1]:\n            if self.isConnected(u, v):\n                removed += 1\n                continue\n            self.union(u, v)\n        \n        if sum([i == self.parent[i] for i in range(1, n + 1)]) > 1:\n            return -1\n        \n        return removed\n    \n    def isConnected(self, a, b):\n        root_a = self.find(a)\n        root_b = self.find(b)\n        return root_a == root_b\n    \n    def union(self, a, b):\n        root_a = self.find(a)\n        root_b = self.find(b)\n        if root_a != root_b:\n            if self.size[root_a] > self.size[root_b]:\n                self.parent[root_b] = root_a\n                self.size[root_a] += self.size[root_b]\n            else:\n                self.parent[root_a] = root_b\n                self.size[root_b] += self.size[root_a]\n            return True\n        return False\n    \n    def find(self, a):\n        curr = a\n        while self.parent[curr] != curr:\n            curr = self.parent[curr]\n        root, curr = curr, a\n        while self.parent[curr] != curr:\n            self.parent[curr], curr = root, self.parent[curr]\n        return root\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        a,b,c=[[] for _ in range(n+1)],[[] for _ in range(n+1)],[[] for _ in range(n+1)]\n        for k,i,j in edges:\n            if k==1:\n                a[i].append(j)\n                a[j].append(i)\n            elif k==2:\n                b[i].append(j)\n                b[j].append(i)\n            else:\n                c[i].append(j)\n                c[j].append(i)\n        d,st=[1]*(n+1),[1]\n        d[0],d[1]=0,0\n        while st:\n            i=st.pop()\n            for j in a[i]:\n                if d[j]:\n                    d[j]=0\n                    st.append(j)\n            for j in c[i]:\n                if d[j]:\n                    d[j]=0\n                    st.append(j)\n        if any(x for x in d): return -1\n        d,st=[1]*(n+1),[1]\n        d[0],d[1]=0,0\n        while st:\n            i=st.pop()\n            for j in b[i]:\n                if d[j]:\n                    d[j]=0\n                    st.append(j)\n            for j in c[i]:\n                if d[j]:\n                    d[j]=0\n                    st.append(j)\n        if any(x for x in d): return -1\n        d,s=[1]*(n+1),0\n        for i in range(1,n+1):\n            if d[i]:\n                st,d[i]=[i],0\n                while st:\n                    i=st.pop()\n                    for j in c[i]:\n                        if d[j]:\n                            d[j],s=0,s+1\n                            st.append(j)\n        return len(edges)-(2*n-2-s)", "class UF:\n    def __init__(self, n):\n        self.par = list(range(n))\n        self.size = [1] * n\n        \n    def find(self, x):\n        if self.par[x] != x:\n            self.par[x] = self.find(self.par[x])\n        return self.par[x]\n    \n    def union(self, x , y):\n        rx, ry = self.find(x), self.find(y)\n        if rx == ry:\n            return False\n        if self.size[rx] < self.size[ry]:\n            rx, ry = ry, rx\n        self.par[ry] = rx\n        self.size[rx] += self.size[ry]\n        self.size[ry] = self.size[rx]\n        return True\n    \n    def sizee(self, x):\n        return self.size[self.find(x)]\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, N: int, edges: List[List[int]]) -> int:\n        for i in range(len(edges)):\n            edges[i][1] -= 1\n            edges[i][2] -= 1\n            \n        alice = []\n        bob = []\n        both = []\n        for t, u, v in edges:\n            if t == 1:\n                alice.append([u, v])\n            elif t == 2:\n                bob.append([u, v])\n            else:\n                both.append([u, v])\n                \n        uf1 = UF(N)\n        uf2 = UF(N)\n        res = 0\n        for u, v in both:\n            res += not uf1.union(u, v)\n            uf2.union(u, v)\n        for u, v in alice:\n            res += not uf1.union(u, v)\n        for u, v in bob:\n            res += not uf2.union(u, v)\n            \n        if uf1.sizee(0) != N or uf2.sizee(0) != N:\n            return -1\n        return res", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        sets = [-1] * (n + 1)\n        \n        def root(v):\n            if sets[v] < 0: return v\n            sets[v] = root(sets[v])\n            return sets[v]\n        \n        def union(u, v):\n            u, v = root(u), root(v)\n            if u == v: return False\n            if sets[u] > sets[v]:\n                u, v = v, u\n            sets[u] += sets[v]\n            sets[v] = u\n            return True\n        \n        remove_edges = 0\n        alice_edges, bob_edges = 0, 0\n        \n        for t, u, v in edges:\n            if t != 3: continue\n            if not union(u, v):\n                remove_edges += 1\n            else:\n                alice_edges += 1\n                bob_edges += 1\n        \n        save_sets = sets[:]\n        \n        for t, u, v in edges:\n            if t != 1: continue\n            if not union(u, v):\n                remove_edges += 1\n            else:\n                alice_edges += 1\n        \n        sets = save_sets\n        \n        for t, u, v in edges:\n            if t != 2: continue\n            if not union(u, v):\n                remove_edges += 1\n            else:\n                bob_edges += 1\n        \n        if bob_edges != n - 1 or alice_edges != n - 1: return -1\n        \n        return remove_edges\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        parenta, parentb = [i for i in range(n)], [i for i in range(n)]\n        def find(i, parent):\n            if parent[i]!=i:\n                parent[i]=find(parent[i], parent)\n            return parent[i]\n        def union(a, b, parent):\n            pa, pb = find(a, parent), find(b, parent)\n            if pa==pb:\n                return False\n            parent[pa]=pb\n            return True\n        added_edge=na=nb=0\n        for typ,u,v in edges:\n            if typ==3 and union(u-1,v-1,parenta) and union(u-1,v-1,parentb):\n                added_edge+=1\n                na+=1\n                nb+=1\n        for typ,u,v in edges:\n            if typ==1 and union(u-1,v-1,parenta):\n                added_edge+=1\n                na+=1\n            elif typ==2 and union(u-1,v-1,parentb):\n                added_edge+=1\n                nb+=1\n        return len(edges)-added_edge if na==nb==n-1 else -1\n                \n            \n", "class DSU:\n    def __init__(self, n: int):\n        self.p = list(range(n))\n        self.e = 0\n        \n    def find(self, x):\n        if x != self.p[x]:\n            self.p[x] = self.find(self.p[x])\n        return self.p[x]\n\n    def merge(self, x, y):\n        rx, ry = self.find(x), self.find(y)\n        if rx == ry: \n            return 1\n        self.p[rx] = ry\n        self.e += 1\n        return 0\n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        A, B = DSU(n + 1), DSU(n + 1)\n        ans = 0\n        for t, x, y in edges:\n            if t != 3:\n                continue\n            ans += A.merge(x, y)\n            B.merge(x, y)\n        \n        for t, x, y in edges:\n            if t == 3: \n                continue\n            d = A if t == 1 else B\n            ans += d.merge(x, y)\n        \n        return ans if A.e == B.e == n - 1 else -1", "class Solution(object):\n    def maxNumEdgesToRemove(self, n, edges):\n\n        ufa = UnionFind(n) # Graph for Alice\n        ufb = UnionFind(n) # Graph for Bob\n        cnt = 0 # number of removable edges\n        \n        for x, y, z in edges:\n            if x == 3:\n                flag1 = ufa.union(y, z)\n                flag2 = ufb.union(y, z)\n                if not flag1 or not flag2: cnt +=1\n\n        for x, y, z in edges:\n            if x == 1:\n                flag = ufa.union(y, z)\n                if not flag: cnt += 1\n            if x == 2:\n                flag = ufb.union(y, z)\n                if not flag: cnt += 1\n\n        return cnt if ufa.groups == 1 and ufb.groups == 1 else -1\n            \n        \nclass UnionFind():\n    def __init__(self, n):\n        self.parents = {i:i for i in range(1, n+1)}\n        self.groups = n\n\n    def find(self, x):\n        if self.parents[x] == x:\n            return x\n        else:\n            self.parents[x] = self.find(self.parents[x])\n            return self.parents[x]\n\n    def union(self, x, y):\n        x = self.find(x)\n        y = self.find(y)\n\n        if x == y:\n            return False\n\n        self.parents[y] = x\n        self.groups -= 1\n        return True\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges = [(t, i - 1, j - 1) for (t, i, j) in edges]\n        bob = [-1 for _ in range(n)]\n        self.ans = 0\n        self.merge(bob, edges, 3)\n        alice = bob.copy()\n        self.merge(bob, edges, 2)\n        self.merge(alice, edges, 1)\n        return -1 if min(alice) != -n or min(bob) != -n else self.ans\n        \n    def merge(self, par, edges, ty):\n        for (t, i, j) in edges:\n            if t == ty:\n                if self.find(par, i) != self.find(par, j):\n                    self.union(par, i, j)\n                else:\n                    self.ans += 1\n                    \n    def find(self, par, i):\n        if par[i] < 0: return i\n        par[i] = self.find(par, par[i])\n        return par[i]\n    \n    def union(self, par, i, j):\n        i, j = self.find(par, i), self.find(par, j)\n        if i == j: return\n        if par[j] < par[i]: i, j = j, i\n        par[i], par[j] = par[i] + par[j], i", "class UnionFind:\n  def __init__(self, n):\n    self.father = list(range(n+1))\n    self.count = n\n    self.size = [0] * (n + 1)\n    \n  def find(self, p):\n    while p != self.father[p]:\n      self.father[p] = p = self.father[self.father[p]]\n    return p\n    \n  def union(self, p, q):\n    p, q = map(self.find, (p, q))\n    if p == q:\n      return False\n    if self.size[p] < self.size[q]:\n      p, q = q, p\n    self.father[q] = p\n    self.size[p] += self.size[q]\n    self.count -= 1\n    return True\n\nclass Solution:\n  def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n    alice, bob, result = UnionFind(n), UnionFind(n), 0\n    for i in range(3, 0, -1):\n      for t, u, v in edges:\n        if i != t:\n          continue\n        if i == 3:\n          x = alice.union(u, v)\n          y = bob.union(u, v)\n          result += not x and not y\n        elif i == 2:\n          result += not bob.union(u, v)\n        else:\n          result += not alice.union(u, v)\n    return result if alice.count == 1 and bob.count == 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        edgesDict = defaultdict(list)\n        \n        for edgeType, src, dst in edges:\n            edgesDict[src].append((edgeType, dst))\n            edgesDict[dst].append((edgeType, src))\n        \n        visited = set()\n        \n        clusters = []\n        cur_cluster = []\n        def DFS(cur):\n            visited.add(cur)\n            cur_cluster.append(cur)\n            for edge in edgesDict[cur]:\n                if edge[0] == 3:\n                    neighbor = edge[1]\n                    if neighbor not in visited:\n                        DFS(neighbor)\n        \n        for i in range(1, n + 1):            \n            if i not in visited:\n                cur_cluster = []\n                DFS(i)\n                clusters.append(cur_cluster)\n        \n        #print('clusters', clusters)\n        \n        nodeToClusterDict = dict()\n        for idx, cur_cluster in enumerate(clusters):\n            for node in cur_cluster:\n                nodeToClusterDict[node] = idx\n        \n        #print('nodeToClusterDict', nodeToClusterDict)\n        clusterEdges = defaultdict(set)\n        \n        def doit(this_type):   \n            clusterEdges = defaultdict(set)\n            for edgeType, src, dst in edges:\n                if edgeType == this_type and nodeToClusterDict[src] != nodeToClusterDict[dst]:\n                    src_cluster, dst_cluster = nodeToClusterDict[src], nodeToClusterDict[dst]\n                    clusterEdges[src_cluster].add(dst_cluster)\n                    clusterEdges[dst_cluster].add(src_cluster)\n            \n            #print('this_type', this_type, 'clusterEdges', clusterEdges)\n            visitedClusters = set()\n            def DFSCluster(cur_cluster):\n                #print('visit cluster', cur_cluster)\n                visitedClusters.add(cur_cluster)\n\n                for neighbor_cluster in clusterEdges[cur_cluster]:\n                    if neighbor_cluster not in visitedClusters:\n                        DFSCluster(neighbor_cluster)\n            \n            DFSCluster(0)\n            if len(visitedClusters) == len(clusters):\n                # all clusters can be visited\n                return len(clusters) - 1\n            else:\n                return -1\n        \n        ans1, ans2 = doit(1), doit(2)\n        \n        if ans1 >= 0 and ans2 >= 0:\n            return len(edges) - (ans1 + ans2 + sum(len(x) - 1 for x in clusters))\n        else:\n            return -1", "class Solution:\n    def maxNumEdgesToRemove(self, n, edges):\n        # Union find\n        def find(node):\n            if parent[node] != node:\n                parent[node] = find(parent[node])\n            return parent[node]\n        \n        def union(node1,node2):\n            node1_p = find(node1)\n            node2_p = find(node2)\n            if node1_p == node2_p:\n                return False\n            if rank[node1_p] > rank[node2_p]:\n                parent[node2_p] = node1_p\n                rank[node1_p] +=1\n            else:\n                parent[node1_p] = node2_p\n                rank[node2_p] +=1\n            return True\n        parent = [i for i in range(n+1)]\n        rank = [0]*(n+1)\n        edge_added = 0 \n        edge_can_remove = 0\n        \n        for edge in edges:\n            if edge[0] == 3:\n                if union(edge[1], edge[2]):\n                    edge_added +=1\n                else:\n                    edge_can_remove +=1\n                    \n        parent_back_up = parent[:]\n        alice_edge = 0\n        bob_edge = 0\n        for edge in edges:\n            if edge[0] == 1:\n                if union(edge[1], edge[2]):\n                    alice_edge +=1\n                else:\n                    edge_can_remove +=1\n        parent = parent_back_up\n        for edge in edges:\n            if edge[0] == 2:\n                if union(edge[1], edge[2]):\n                    bob_edge +=1\n                else:\n                    edge_can_remove +=1\n        if alice_edge == bob_edge == n-1 - edge_added and edge_can_remove>=0:\n            return edge_can_remove\n        return -1", "class Solution:\n       def maxNumEdgesToRemove(self, n, edges):\n        # Union find\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n\n        res = e1 = e2 = 0\n\n        # Alice and Bob\n        root = [i for i in range(n + 1)]\n        for t, i, j in edges:\n            if t == 3:\n                if uni(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        root0 = root[:]\n\n        # only Alice\n        for t, i, j in edges:\n            if t == 1:\n                if uni(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n\n        # only Bob\n        root = root0\n        for t, i, j in edges:\n            if t == 2:\n                if uni(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if e1 == e2 == n - 1 else -1\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(roots, x):\n            if x == roots[x]:\n                return x\n            else:\n                roots[x] = find(roots, roots[x])\n                return roots[x]\n\n        pairs1 = []\n        pairs2 = []\n        pairs3 = []\n\n        for type, x, y in edges:\n            if type == 1:\n                pairs1.append((x, y))\n            elif type == 2:\n                pairs2.append((x, y))\n            else:\n                pairs3.append((x, y))\n\n        roots = [i for i in range(n + 1)]\n        rootSet = set(range(1, n + 1))\n        res = 0\n\n        for x, y in pairs3:\n            root1 = find(roots, x)\n            root2 = find(roots, y)\n\n            if root1 != root2:\n                roots[root2] = root1\n                rootSet.remove(root2)\n            else:\n                res += 1\n\n        root1Set = set(rootSet)\n        root2Set = set(rootSet)\n        roots1 = list(roots)\n        roots2 = list(roots)\n\n        for x, y in pairs1:\n            root1 = find(roots1, x)\n            root2 = find(roots1, y)\n\n            if root1 != root2:\n                roots1[root2] = root1\n                root1Set.remove(root2)\n            else:\n                res += 1\n\n        for x, y in pairs2:\n            root1 = find(roots2, x)\n            root2 = find(roots2, y)\n\n            if root1 != root2:\n                roots2[root2] = root1\n                root2Set.remove(root2)\n            else:\n                res += 1\n\n        if len(root1Set) != 1 or len(root2Set) != 1: return -1\n        return res", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def dfs(s, mp):\n            for n in mp[s]:\n                if visited[n] == 0:\n                    visited[n] = 1\n                    self.cnt += 1\n                    dfs(n, mp)\n\n        c = [0] * 4\n        mpa = defaultdict(set)\n        mpb = defaultdict(set)\n        mpc = defaultdict(set)\n        for e0, e1, e2 in edges:\n            c[e0] += 1\n            if e0 == 1 or e0 == 3:\n                mpa[e1].add(e2)\n                mpa[e2].add(e1)\n            if e0 == 2 or e0 == 3:\n                mpb[e1].add(e2)\n                mpb[e2].add(e1)\n            if e0 == 3:\n                mpc[e1].add(e2)\n                mpc[e2].add(e1)\n                \n        self.cnt = 1\n        visited = [0] * (n + 1)\n        visited[1] = 1\n        dfs(1, mpa)\n        if self.cnt != n:\n            return -1\n\n        self.cnt = 1\n        visited = [0] * (n + 1)\n        visited[1] = 1\n        dfs(1, mpb)\n        if self.cnt != n:\n            return -1\n        \n        uc = 0\n        visited = [0] * (n + 1)\n        for i in range(1, n + 1):\n            if visited[i] == 0:\n                visited[i] = 1\n                self.cnt = 0\n                dfs(i, mpc)\n                uc += self.cnt\n        return (c[3] - uc) + (c[1] + uc - (n-1)) + (c[2] + uc - (n-1))     \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def dfs_and_cluster(n, adj):\n            num_clusters = 0\n            edges_used = 0\n            node_to_cluster = {}\n            start_nodes = list(range(n))\n            while len(start_nodes) > 0:\n                node = start_nodes.pop()\n                if node in node_to_cluster:\n                    continue\n\n                cluster_id = num_clusters\n                num_clusters += 1\n\n                node_to_cluster[node] = cluster_id\n                q = [node]\n                while len(q) > 0:\n                    node = q.pop()\n                    for next_node in adj[node]:\n                        if next_node not in node_to_cluster:\n                            edges_used += 1\n                            node_to_cluster[next_node] = cluster_id\n                            q.append(next_node)\n            return num_clusters, node_to_cluster, edges_used\n\n        shared_adj = {i: set() for i in range(n)}\n        for edge_type, u, v in edges:\n            u, v = u - 1, v - 1\n            if edge_type == 3:\n                shared_adj[u].add(v)\n                shared_adj[v].add(u)\n\n        shared_num_clusters, shared_node_to_cluster, shared_edges_used = dfs_and_cluster(n, shared_adj)\n        print(shared_node_to_cluster)\n\n        alice_adj = {i: set() for i in range(shared_num_clusters)}\n        for edge_type, u, v in edges:\n            u, v = u - 1, v - 1\n            if edge_type == 1:\n                u = shared_node_to_cluster[u]\n                v = shared_node_to_cluster[v]\n                alice_adj[u].add(v)\n                alice_adj[v].add(u)\n\n        alice_num_clusters, _, alice_edges_used = dfs_and_cluster(shared_num_clusters, alice_adj)\n\n        bob_adj = {i: set() for i in range(shared_num_clusters)}\n        for edge_type, u, v in edges:\n            u, v = u - 1, v - 1\n            if edge_type == 2:\n                u = shared_node_to_cluster[u]\n                v = shared_node_to_cluster[v]\n                bob_adj[u].add(v)\n                bob_adj[v].add(u)\n        \n        bob_num_clusters, _, bob_edges_used = dfs_and_cluster(shared_num_clusters, bob_adj)\n\n        if alice_num_clusters > 1 or bob_num_clusters > 1:\n            return -1\n\n        return len(edges) - shared_edges_used - alice_edges_used - bob_edges_used\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges) -> int:\n        N = len(edges)\n        dup = set()\n        res = 0\n        c1, c2, bc = 0, 0, 0\n        alice, bob, both = defaultdict(list), defaultdict(list), defaultdict(list)\n        \n        for t, u, v in edges:\n            if (t, u, v) not in dup:\n                dup.add((t, u, v))\n                if t == 1 or t == 3:\n                    if t == 1:\n                        c1 += 1\n                    alice[u].append(v)\n                    alice[v].append(u)\n                if t == 2 or t == 3:\n                    if t == 2:\n                        c2 += 1\n                    bob[u].append(v)\n                    bob[v].append(u)\n                if t == 3:\n                    bc += 1\n                    both[u].append(v)\n                    both[v].append(u)\n            else:\n                res += 1\n        \n        va, vb, = set(), set()\n        vc = dict()\n        \n        def dfs(node, t):\n            if t == 1:\n                va.add(node)\n                for ngb in alice[node]:\n                    if not ngb in va:\n                        dfs(ngb, t)\n            else:\n                vb.add(node)\n                for ngb in bob[node]:\n                    if not ngb in vb:\n                        dfs(ngb, t)\n        \n        dfs(1, 1)\n        dfs(1, 2)\n        \n        if len(va) < n or len(vb) < n:\n            return -1\n        \n        def dfs_both(node, prev, idx):\n            vc[node] = idx\n            self.tmp += 1\n            for ngb in both[node]:\n                if ngb == prev:\n                    continue\n                if ngb not in vc:\n                    dfs_both(ngb, node, idx)\n         \n        bc_need = 0\n        idx = 0\n        for i in both:\n            if i not in vc:\n                idx += 1\n                self.tmp = 0\n                dfs_both(i, -1, idx)\n                bc_need += self.tmp - 1\n                \n        res += bc - bc_need\n        res += c1 - (n - 1 - bc_need)\n        res += c2 - (n - 1 - bc_need)\n        return res", "from collections import defaultdict\nfrom heapq import heappush, heappop\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        aj = [defaultdict(set) for i in range(4)]\n        total = len(edges)\n        for t, i, j in edges:\n            if i == j:\n                continue\n            aj[t][i].add(j)\n            aj[t][j].add(i)\n        \n        reuse = set()\n        count = 0\n        \n        visited = {1}\n        heap = []\n        for i in aj[3][1]:\n            heappush(heap, (1, 1, i))\n        for i in aj[1][1]:\n            heappush(heap, (2, 1, i))\n        while len(visited) < n and heap:\n            w, i, j = heappop(heap)\n            if j in visited:\n                continue\n                \n            if w == 1:\n                reuse.add((i, j))\n            count += 1\n            visited.add(j)\n            for k in aj[3][j]:\n                if k not in visited:\n                    heappush(heap, (1, j, k))\n            for k in aj[1][j]:\n                if k not in visited:\n                    heappush(heap, (2, j, k))\n        if len(visited) < n:\n            return -1\n            \n        visited = {1}\n        heap = []\n        for i in aj[3][1]:\n            if (1, i) in reuse or (i, 1) in reuse:\n                heappush(heap, (0, 1, i))\n            else:\n                heappush(heap, (1, 1, i))\n        for i in aj[2][1]:\n            heappush(heap, (2, 1, i))\n        while len(visited) < n and heap:\n            w, i, j = heappop(heap)\n            if j in visited:\n                continue\n                \n            if w > 0:\n                count += 1\n            visited.add(j)\n            for k in aj[3][j]:\n                if k not in visited:\n                    if (j, k) in reuse or (k, j) in reuse:\n                        heappush(heap, (0, j, k))\n                    else:\n                        heappush(heap, (1, j, k))\n            for k in aj[2][j]:\n                if k not in visited:\n                    heappush(heap, (2, j, k))\n        if len(visited) < n:\n            return -1\n\n        return total - count\n", "import copy\n\nfrom collections import defaultdict\n\nclass DSU:\n  def __init__(self, reps):\n    # representer\n    self.reps = reps\n  # def add(self, x):\n  #   self.reps[x] = x\n  def find(self, x):\n    if not x == self.reps[x]:\n      self.reps[x] = self.find(self.reps[x])\n    return self.reps[x]\n  def union(self, x, y):\n    self.reps[self.find(y)] = self.find(x)\n\nclass Solution:\n  def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n    # start with all type3 edges\n    es = [[], [], [], []]\n    for i, u, v in edges:\n      es[i].append((u, v))\n    # start with all type3 edges\n    dsu = DSU(reps = {x: x for x in range(1, n + 1)})\n    for u, v in es[3]:\n      dsu.union(u, v)\n    # islands\n    islands = defaultdict(set)\n    for x in range(1, n + 1):\n      islands[dsu.find(x)].add(x)\n    if len(islands) == 1:\n      return len(es[3]) - (n - 1) + len(es[1]) + len(es[2])\n    # Alice\n    dA = copy.deepcopy(dsu)\n    for u, v in es[1]:\n      dA.union(u, v)\n    islandsA = defaultdict(set)\n    for x in range(1, n + 1):\n      islandsA[dA.find(x)].add(x)\n    if len(islandsA) > 1:\n      return -1\n    # Bob\n    dB = copy.deepcopy(dsu)\n    for u, v in es[2]:\n      dB.union(u, v)\n    islandsB = defaultdict(set)\n    for x in range(1, n + 1):\n      islandsB[dB.find(x)].add(x)\n    if len(islandsB) > 1:\n      return -1\n    return len(edges) - (n - len(islands)) - (len(islands) - 1) * 2", "class Solution:\n    def maxNumEdgesToRemove(self, N: int, E: List[List[int]], same = 0) -> int:\n        E = [[_, u - 1, v - 1] for _, u, v in E]                    # \u2b50\ufe0f -1 for 1-based to 0-based indexing\n        A = [i for i in range(N)]                                   # \\\\U0001f642 parent representatives of disjoint sets for Alice\n        B = [i for i in range(N)]                                   # \\\\U0001f642 parent representatives of disjoint sets for Bob\n        def find(P, x): P[x] = P[x] if P[x] == x else find(P, P[x]); return P[x]\n        def union(P, a, b):\n            a = find(P, a)\n            b = find(P, b)\n            if a == b:\n                return 1\n            P[a] = b  # arbitrary choice\n            return 0\n        for type, u, v in E:\n            if type == 3: same += union(A, u, v) | union(B, u, v)   # \\\\U0001f947 first: \\\\U0001f517 union \u2705 shared edges between Alice and Bob\n        for type, u, v in E:\n            if type == 1: same += union(A, u, v)                    # \\\\U0001f948 second: \\\\U0001f517 union \\\\U0001f6ab non-shared edges between Alice and Bob\n            if type == 2: same += union(B, u, v)\n        return same if all(find(A, 0) == find(A, x) for x in A) and all(find(B, 0) == find(B, x) for x in B) else -1\n        \n", "class Solution:\n    def find(self, x, uf):\n        if uf[x] != x:\n            uf[x] = self.find(uf[x], uf)\n        return uf[x]\n    \n    def union(self, u, v, uf):\n        p1 = self.find(u, uf)\n        p2 = self.find(v, uf)\n        uf[p1] = p2\n        return p1 != p2\n    \n    def connected(self, uf):\n        parent = set()\n        for i in range(len(uf)):\n            parent.add(self.find(i, uf))\n            if len(parent) > 1:\n                return False\n        return len(parent) == 1\n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges = [[t, u-1, v-1] for t, u, v in edges]\n        t1_edges = set()\n        t2_edges = set()\n        t3_edges = set()\n        ans = 0\n        \n        for t, u, v in edges:\n            if t == 1:\n                t1_edges.add((u, v))\n            elif t == 2:\n                t2_edges.add((u, v))\n            else:\n                if (u, v) in t1_edges:\n                    t1_edges.remove((u, v))\n                    ans += 1\n                if (u, v) in t2_edges:\n                    t2_edges.remove((u, v))\n                    ans += 1\n                t3_edges.add((u, v))\n        \n        uf1 = [i for i in range(n)]\n        uf2 = [i for i in range(n)]\n        \n        for u, v in t3_edges:\n            union1 = self.union(u, v, uf1)\n            union2 = self.union(u, v, uf2)\n            if not union1 and not union2:\n                ans += 1\n        \n        for u, v in t1_edges:\n            if not self.union(u, v, uf1):\n                ans += 1\n        for u, v in t2_edges:\n            if not self.union(u, v, uf2):\n                ans += 1\n                \n        if not self.connected(uf1) or not self.connected(uf2):\n            return -1\n        return ans\n        \n        \n", "import copy\n\ndef union(subsets, u, v):\n    uroot = find(subsets, u)\n    vroot = find(subsets, v)\n    \n    if subsets[uroot][1] > subsets[vroot][1]:\n        subsets[vroot][0] = uroot\n    if subsets[vroot][1] > subsets[uroot][1]:\n        subsets[uroot][0] = vroot\n    if subsets[uroot][1] == subsets[vroot][1]:\n        subsets[vroot][0] = uroot\n        subsets[uroot][1] += 1\n    \n\ndef find(subsets, u):\n    if subsets[u][0] != u:\n        subsets[u][0] = find(subsets, subsets[u][0])\n    return subsets[u][0]\n\n\nclass Solution:\n    #kruskal's\n    #1 is alice and 2 is bob\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        mst1 = set() #set for Alice's MST\n        mst2 = set() #set for Bob's MST\n        subsets1 = ['1 index'] + [[x+1,0] for x in range(n)] #Alice's unionfind\n        subsets2 = ['1 index'] + [[x+1,0] for x in range(n)] #Bob's unionfind\n        \n        edges = sorted(edges, key= lambda e: -e[0])\n        e = 0 #number of total edges used\n        e1 = 0 #number of edges for Alice\n        e2 = 0 #number of edges for Bob\n        i = 0 #track position in edges list\n        \n        #start with type 3 edges\n        while e < n - 1:\n            if i == len(edges): \n                return -1\n            typ, u, v = edges[i]\n            if typ != 3: break\n            if find(subsets1, u) != find(subsets1, v):\n                union(subsets1, u, v)\n                mst1.add(u)\n                mst1.add(v)\n                e += 1\n            \n            i += 1\n        \n        #everything that was done to Alice applies to Bob\n        e1 = e\n        e2 = e\n        mst2 = mst1.copy()\n        subsets2 = copy.deepcopy(subsets1)\n        \n        #once done with shared edges, do Bob's\n        while e2 < n-1:\n            if i == len(edges): \n                return -1\n            typ, u, v = edges[i]\n            if typ != 2: break\n            if find(subsets2, u) != find(subsets2, v):\n                union(subsets2, u, v)\n                e += 1\n                e2 += 1\n            i += 1\n        \n        if e2 < n - 1: \n            return -1 #if we've used all edges bob can use (types 2 and 3) and he still can't reach all nodes, ur fucked\n        \n        #now finish Alice's MST\n        while e1 < n-1:\n            if i == len(edges): \n                return -1\n            \n            typ, u, v = edges[i]\n            if find(subsets1, u) != find(subsets1, v):\n                union(subsets1, u, v)\n                e += 1\n                e1 += 1\n            i += 1\n            \n        return len(edges) - e\n            \n            \n            \n            \n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        edges.sort(key=lambda x:-x[0])\n        \n        \n        father1=[i for i in range(n+1)]\n        size1=[1 for i in range(n+1)]\n        father2=[i for i in range(n+1)]\n        size2=[1 for i in range(n+1)]\n        \n        \n        def find(a,typ):\n            \n            if typ==1:\n                \n                path=[]\n                \n                while a!=father1[a]:\n                    path.append(a)\n                    a=father1[a]\n                    \n                \n                for b in path:\n                    father1[b]=a\n                    \n                return a\n            \n            elif typ==2:\n                \n                path=[]\n                \n                while a!=father2[a]:\n                    path.append(a)\n                    a=father2[a]\n                    \n                \n                for b in path:\n                    father2[b]=a\n                    \n                return a\n                \n            \n        def union(a,b,typ):\n            \n            fa, fb = find(a,typ), find(b,typ)\n            if fa==fb:\n                return\n            \n            if typ==1:\n                \n                father1[fa]=fb\n                size1[fb]+=size1[fa]\n                \n            else:\n                \n                father2[fa]=fb\n                size2[fb]+=size2[fa]\n                \n            return\n        \n        \n        necessary=0\n        \n        for edge in edges:\n            typ,a,b=edge\n            \n            if typ==3:\n                \n                if find(a,1)!=find(b,1):\n                    \n                    necessary+=1\n                    \n                    union(a,b,1)\n                    union(a,b,2)\n            \n            elif typ==1:\n                \n                if find(a,1)!=find(b,1):\n                    \n                    necessary+=1\n                    \n                    union(a,b,1)\n            \n            else:\n                \n                if find(a,2)!=find(b,2):\n                    \n                    necessary+=1\n                    \n                    union(a,b,2)\n                    \n        \n        if size1[find(1,1)]!=n or size2[find(1,2)]!=n:\n            return -1\n        \n        return len(edges)-necessary\n                \n                    \n", "class UnionFind:\n    def __init__(self,n):\n        self.parent=[i for i in range(n+1)]\n        self.size=[1]*(n+1)\n    def find(self,x):\n        if self.parent[x]==x:\n            return x\n        self.parent[x]=self.find(self.parent[x])\n        return self.parent[x]\n    def union(self,x,y):\n        x=self.find(x)\n        y=self.find(y)\n        if x==y:\n            return False,0\n        if self.size[x]>self.size[y]:\n            x,y=y,x\n        self.parent[y]=x\n        self.size[x]+=self.size[y]\n        return True,self.size[x]\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def getMST(whos):\n            nonlocal edges,n\n            unionfind=UnionFind(n)\n            # mst=set()\n            reqEdges=set()\n            nodes=0\n            for indx,(typ,fnode,tnode) in enumerate(edges):\n                # if fnode not in mst or tnode not in mst:\n                siz=0\n                if typ==3 or typ==whos:\n                    res,siz=unionfind.union(fnode,tnode)\n                    if res:\n                        reqEdges.add(tuple(edges[indx]))\n                        nodes=max(nodes,siz)\n                if siz==n:\n                    break\n            return siz==n,reqEdges\n        \n        edges.sort(key=lambda item: -item[0])\n        ares,alice=getMST(1)\n        if not ares:\n            return -1\n        bres,bob=getMST(2)\n        if not bres:\n            return -1\n        nset=alice.union(bob)\n        return len(edges)-len(nset)\n        \n                        \n                        \n        \n        \n        \n        \n            \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, e: List[List[int]]) -> int:\n        g=[[] for _ in range(n)]\n        for t,x,y in e:\n            g[x-1].append((y-1,t))\n            g[y-1].append((x-1,t))\n\n        def sub(f):\n            res=[0]\n            s=[0]\n            def dfs(now,q):\n                if 1<<now&s[0]:return \n                s[0]|=1<<now\n                if q:\n                    res[0]+=1\n                for nex,t in g[now]:\n                    if t==f or t==3:\n                        dfs(nex,0)\n            for i in range(n):\n                dfs(i,1)\n            return res[0]\n\n        if sub(1)>1 or sub(2)>1:\n            return -1\n        return len(e)-(n-2+sub(3))\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, A: List[List[int]]) -> int:\n        g = collections.defaultdict(list)\n        \n        self.vis = {}\n        \n        compIdx = {}\n        \n        def dfs(node, typ, compIndex, mapRef = compIdx):\n            mapRef[node] = compIndex\n            self.vis[node] = 1\n            for child, eType in g[node]:\n                if eType in typ and child not in self.vis:\n                    dfs(child, typ, compIndex, mapRef)\n            self.vis[node] = 2\n\n        ctr3 = 0\n        ctr1, ctr2 = 0, 0\n        set1 = set()\n        set2 = set()\n        set3 = set()\n        for k,i,j in A:\n            if k == 3:\n                ctr3 += 1\n                ctr1 += 1\n                ctr2 += 1\n                set3.add(i)\n                set3.add(j)\n            elif k == 1:\n                ctr1 += 1\n                set1.add(i)\n                set1.add(j)\n            else:\n                ctr2 += 1\n                set2.add(i)\n                set2.add(j)\n            g[i].append((j,k))\n            g[j].append((i,k))\n        \n        comp3 = 0\n        for node in set3:\n            if node not in self.vis:\n                dfs(node, [3], comp3)\n                comp3 += 1\n        \n        # Deletable edges of type 3\n        res1 = ctr3 - ((len(set3)) - comp3)\n        \n        comp1Idx = {}\n        comp1 = 0\n        self.vis = {}\n        for node in set1.union(set3):\n            if node not in self.vis:\n                dfs(node, [1, 3], comp1, comp1Idx)\n                comp1 += 1\n        \n        comp2Idx = {}\n        comp2 = 0\n        self.vis = {}\n        for node in set2.union(set3):\n            if node not in self.vis:\n                dfs(node, [2, 3], comp2, comp2Idx)\n                comp2 += 1\n        \n        print(comp1, comp2, comp3, len(comp1Idx), len(comp2Idx), len(compIdx), len(A))\n        \n        if comp1 > 1 or comp2 > 1 or len(comp1Idx) != n or len(comp2Idx) != n:\n            return -1\n        \n        res2 = ctr1 - ((len(set3.union(set1))) - comp1)\n        res3 = ctr2 - ((len(set3.union(set2))) - comp2)\n        \n        print(res1, res2, res3)\n        return - res1 + res2 + res3\n    \n    \n\\\"\\\"\\\"\n13\n[[1,1,2],[2,1,3],[3,2,4],[3,2,5],[1,2,6],[3,6,7],[3,7,8],[3,6,9],[3,4,10],[2,3,11],[1,5,12],[3,3,13],[2,1,10],[2,6,11],[3,5,13],[1,9,12],[1,6,8],[3,6,13],[2,1,4],[1,1,13],[2,9,10],[2,1,6],[2,10,13],[2,2,9],[3,4,12],[2,4,7],[1,1,10],[1,3,7],[1,7,11],[3,3,12],[2,4,8],[3,8,9],[1,9,13],[2,4,10],[1,6,9],[3,10,13],[1,7,10],[1,1,11],[2,4,9],[3,5,11],[3,2,6],[2,1,5],[2,5,11],[2,1,7],[2,3,8],[2,8,9],[3,4,13],[3,3,8],[3,3,11],[2,9,11],[3,1,8],[2,1,8],[3,8,13],[2,10,11],[3,1,5],[1,10,11],[1,7,12],[2,3,5],[3,1,13],[2,4,11],[2,3,9],[2,6,9],[2,1,13],[3,1,12],[2,7,8],[2,5,6],[3,1,9],[1,5,10],[3,2,13],[2,3,6],[2,2,10],[3,4,11],[1,4,13],[3,5,10],[1,4,10],[1,1,8],[3,3,4],[2,4,6],[2,7,11],[2,7,10],[2,3,12],[3,7,11],[3,9,10],[2,11,13],[1,1,12],[2,10,12],[1,7,13],[1,4,11],[2,4,5],[1,3,10],[2,12,13],[3,3,10],[1,6,12],[3,6,10],[1,3,4],[2,7,9],[1,3,11],[2,2,8],[1,2,8],[1,11,13],[1,2,13],[2,2,6],[1,4,6],[1,6,11],[3,1,2],[1,1,3],[2,11,12],[3,2,11],[1,9,10],[2,6,12],[3,1,7],[1,4,9],[1,10,12],[2,6,13],[2,2,12],[2,1,11],[2,5,9],[1,3,8],[1,7,8],[1,2,12],[1,5,11],[2,7,12],[3,1,11],[3,9,12],[3,2,9],[3,10,11]]\n\\\"\\\"\\\"", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(x):\n            if x != root[x]:\n                root[x] = find(root[x])\n            return root[x]\n        \n        e1 = 0\n        e2 = 0\n        res = 0\n        def union(x, y):\n            x = find(x)\n            y = find(y)\n            if x == y:\n                return 0\n            root[x] = y\n            return 1\n        \n        root = list(range(n+1))\n        for t, i, j in edges:\n            if t == 3:\n                if union(i,j):\n                    e1+=1\n                    e2+=1\n                else:\n                    res+=1\n        temp = root[:]\n        \n        for t,i,j in edges:\n            if t == 1:\n                if union(i,j):\n                    e1+=1\n                else:\n                    res+=1\n        \n        root = temp\n        \n        for t,i,j in edges:\n            if t == 2:\n                if union(i,j):\n                    e2+=1\n                else:\n                    res+=1\n        \n        if e1 == n-1 and e2 == n-1:\n            return res\n        else:\n            return -1", "class Dsu:\n    def __init__(self, n):\n        self.roots = list(range(n + 1))\n        self.cnts = [1] * (n + 1)\n            \n    def find(self, x):\n        if self.roots[x] != x:\n            self.roots[x] = self.find(self.roots[x])\n        return self.roots[x]\n        \n    def union(self, x, y):\n        rx = self.find(x)\n        ry = self.find(y)\n        if rx == ry:\n            return False\n        if self.cnts[rx] >= self.cnts[ry]:\n            self.roots[ry] = rx\n            self.cnts[rx] += self.cnts[ry]\n            self.cnts[ry] = 0\n        else:\n            self.roots[rx] = ry\n            self.cnts[ry] += self.cnts[rx]\n            self.cnts[rx] = 0\n        return True\n        \nclass Solution:\n            \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # [[3,1,2],[3,2,3],[1,1,3],[1,2,4],[1,1,2],[2,3,4]]\n        dsu_alice = Dsu(n)\n        dsu_bob = Dsu(n)\n        rm = 0\n        for t, u, v in edges:\n            if t == 3:\n                dsu_alice.union(u, v)\n                if not dsu_bob.union(u, v):\n                    rm += 1\n                    \n        for t, u, v in edges:\n            if t == 1:\n                if not dsu_alice.union(u, v):\n                    rm += 1\n            elif t == 2:\n                if not dsu_bob.union(u, v):\n                    rm += 1\n                    \n        if dsu_alice.cnts[dsu_alice.find(1)] != n or dsu_bob.cnts[dsu_bob.find(1)] != n:\n            return -1\n        return rm", "class UF:\n    def __init__(self, n):\n        self.p = [i for i in range(n)]\n        \n    def find(self, x):\n        if self.p[x] != x:\n            self.p[x] = self.find(self.p[x])\n        return self.p[x]\n    \n    def union(self, x, y):\n        px, py = self.find(x), self.find(y)\n        if px == py:\n            return False\n        self.p[py] = px\n        return True\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        A, B = set(), set()\n        rmA, rmB = 0, 0\n        for t, u, v in edges:\n            if t == 1:\n                if (-3, u, v) in A:\n                    rmA += 1\n                else:\n                    A.add((-1, u, v))\n            elif t == 2:\n                if (-3, u, v) in B:\n                    rmB += 1\n                else:\n                    B.add((-2, u, v))\n            else:\n                if (-1, u, v) in A:\n                    rmA += 1\n                    A.remove((-1, u, v))\n                if (-2, u, v) in B:\n                    rmB += 1  \n                    B.remove((-2, u, v))\n                A.add((-3, u, v))\n                B.add((-3, u, v))\n        \n        common = set()\n        ufa = UF(n + 1)\n        ufb = UF(n + 1)\n        eA = eB = 0\n        for t, u, v in sorted(A):\n            if ufa.union(u, v):\n                eA += 1\n            else:\n                if t == -1:\n                    rmA += 1\n                else:\n                    common.add((u, v))\n                    \n        for t, u, v in sorted(B):\n            if ufb.union(u, v):\n                eB += 1\n            else:\n                if t == -2:\n                    rmB += 1\n                else:\n                    common.add((u, v))\n                   \n        return rmA + rmB + len(common) if eA == eB == n - 1 else -1\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(x, p):\n            if p[x] != x:\n                p[x] = find(p[x], p)\n            return p[x]\n        \n        def merge(rx, ry, p):\n            p[rx] = ry\n        \n        res = 0\n        pa = [i for i in range(n)]\n        pb = [i for i in range(n)]\n        \n        edges.sort(key=lambda x: -x[0])\n        \n        for type_, u, v in edges:\n            if type_ == 1:\n                ru, rv = find(u - 1, pa), find(v - 1, pa)\n                if ru == rv:\n                    res += 1\n                else:\n                    merge(ru, rv, pa)\n            elif type_ == 2:    \n                ru, rv = find(u - 1, pb), find(v - 1, pb)\n                if ru == rv:\n                    res += 1\n                else:\n                    merge(ru, rv, pb)\n            \n            if type_ == 3:\n                rua, rva = find(u - 1, pa), find(v - 1, pa)\n                rub, rvb = find(u - 1, pb), find(v - 1, pb)\n                if rua == rva and rub == rvb:\n                    res += 1\n                else:\n                    merge(rua, rva, pa)\n                    merge(rub, rvb, pb)\n                    \n        \n        return res if len({find(i, pa) for i in range(n)}) == len({find(i, pb) for i in range(n)}) == 1 else -1\n", "class Solution:\n    def maxNumEdgesToRemove(self, n, edges):\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return False\n            root[x] = y\n            return True\n\n        root = {i:i for i in range(1,n+1)}\n        res, a, b = 0, 0, 0\n        \n        for t, u, v in edges:\n            if t == 3:\n                if uni(u, v):\n                    a += 1\n                    b += 1\n                else:\n                    res += 1\n                    \n        common = root.copy()\n        for t, u, v in edges:\n            if t == 1:\n                if uni(u, v):\n                    a += 1\n                else:\n                    res += 1\n        \n        root = common\n        for t, u, v in edges:\n            if t == 2:\n                if uni(u, v):\n                    b += 1\n                else:\n                    res += 1\n        return res if a == b == n - 1 else -1\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:        \n        p1, s1 = list(range(n + 1)), [1] * (n + 1)\n        inc = 0\n        \n        def find(parent, i): \n            if parent[i] != i: \n                parent[i] = find(parent, parent[i])\n            return parent[i]\n\n        def union(parent, size, x, y): \n            i, j = find(parent, x), find(parent, y)\n            if size[i] < size[j]: \n                parent[i] = j\n                size[j] += size[i]\n            else: \n                parent[j] = i \n                size[i] += size[j]\n        \n        for t, u, v in edges:\n            if t == 3:\n                i, j = find(p1, u), find(p1, v)\n                if i == j: continue\n                union(p1, s1, i, j)\n                inc += 1\n        p2, s2 = p1[:], s1[:]        \n        \n        for t, u, v in edges:\n            if t == 1:\n                i, j = find(p1, u), find(p1, v)\n                if i == j: continue\n                union(p1, s1, i, j)\n                inc += 1\n            elif t == 2:\n                i, j = find(p2, u), find(p2, v)\n                if i == j: continue\n                union(p2, s2, i, j)\n                inc += 1\n        \n        return len(edges) - inc if max(s1) == n and max(s2) == n else -1", "class UnionFind:\n    def __init__(self):\n        self.parents = defaultdict(lambda:-1)\n        self.ranks = defaultdict(lambda:1)\n    def join(self,a,b):\n        pa,pb = self.find(a),self.find(b)\n        if pa==pb:\n            return False\n        if self.ranks[pa]>self.ranks[pb]:\n            self.parents[pb]=pa\n            self.ranks[pa]+=self.ranks[pb]\n        else:\n            self.parents[pa]=pb\n            self.ranks[pb]+=self.ranks[pa]\n        return True\n    def find(self,a):\n        if self.parents[a]==-1:\n            return a\n        self.parents[a]=self.find(self.parents[a])\n        return self.parents[a]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        if n==0:\n            return 0\n        def pre_check(person):\n            my_edges = [edge for edge in edges if edge[0] in [person,3]]\n            dic = defaultdict(list)\n            for _,a,b in my_edges:\n                dic[a].append(b)\n                dic[b].append(a)\n            seen=set()\n            # print(dic)\n            def dfs(curr):\n                # print(curr)\n                if curr in seen:\n                    return\n                seen.add(curr)\n                for nxt in dic[curr]:\n                    # print('nxt',nxt)\n                    dfs(nxt)\n            dfs(1)\n            # print(seen, dic)\n            # print(len(seen))\n            return len(seen)==n\n        if not pre_check(1) or not pre_check(2):\n            return -1\n        both_edges = [edge for edge in edges if edge[0]==3]\n        # print(both_edges)\n        a_cnt = sum(edge[0]==1 for edge in edges)\n        b_cnt = sum(edge[0]==2 for edge in edges)\n        uf = UnionFind()\n        rid = 0\n        for edge in both_edges:\n            # print(edge)\n            if not uf.join(edge[1],edge[2]):\n                rid += 1\n        uniq = set(uf.find(i) for i in range(1,n+1))\n        uniq = len(uniq)\n        # print(rid,uniq)\n        # print(uniq)\n        # print(rid)\n        # print(a_cnt, b_cnt)\n        return rid + a_cnt - uniq + 1 + b_cnt - uniq + 1\n        \n            \n        \n", "import collections\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        #remove duplicate if type3 exists remove type1 and type2\n        type1_graph=collections.defaultdict(set)\n        type2_graph=collections.defaultdict(set)\n        type3_graph=collections.defaultdict(set)\n        alice_graph=collections.defaultdict(set)\n        bob_graph=collections.defaultdict(set)\n        no_of_type1_edges=0\n        no_of_type2_edges=0\n        no_of_type3_edges=0\n        for t,v1,v2 in edges:\n            if t==3:\n                type3_graph[v1].add(v2)\n                type3_graph[v2].add(v1)\n                alice_graph[v1].add(v2)\n                alice_graph[v2].add(v1)\n                bob_graph[v1].add(v2)\n                bob_graph[v2].add(v1)\n                no_of_type3_edges +=1\n\n            if t==1:\n                type1_graph[v1].add(v2)\n                type1_graph[v2].add(v1)\n                no_of_type1_edges +=1\n                alice_graph[v1].add(v2)\n                alice_graph[v2].add(v1)\n\n            if t==2:\n                type2_graph[v1].add(v2)\n                type2_graph[v2].add(v1)\n                no_of_type2_edges +=1\n                bob_graph[v1].add(v2)\n                bob_graph[v2].add(v1)\n\n        def dfs(s,edges,visited):\n            for e in edges[s]:\n                if e not in visited:\n                    visited.add(e)\n                    dfs(e,edges,visited)\n\n\n        def tran_graph(edges):\n            nodes_set=[]\n            total_visited=set()\n            if len(edges)==0:\n                return []\n            for s in edges.keys():\n                if s not in total_visited:\n                    visited=set()\n                    dfs(s,edges,visited)\n                    nodes_set.append(visited)\n                    total_visited|=visited\n            print(nodes_set)\n            return nodes_set\n        # check whether alice and bob's graph connected\n        for nodes_set in (tran_graph(alice_graph),tran_graph(bob_graph)):\n            if len(nodes_set)!=1:\n                return -1\n            if len(nodes_set[0])!=n:\n                return -1\n        #remove duplicate type edge\n        type3_nodes_sets=tran_graph(type3_graph)\n        print(\\\"type3_nodes_sets\\\",type3_nodes_sets)\n        print(\\\"type3 edges\\\",no_of_type3_edges)\n        type3_nodes=0\n        removed=no_of_type3_edges\n        for set_ in type3_nodes_sets:\n            type3_nodes+=len(set_)\n            removed-=len(set_)-1\n        print(removed)\n\n\n        graphs=len(type3_nodes_sets)\n        removed+=no_of_type1_edges-(graphs-1+n-type3_nodes)\n        removed += no_of_type2_edges - (graphs - 1 + n - type3_nodes)\n        # print(removed)\n        return removed\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        A, B = {i:i for i in range(1,n+1)}, {i:i for i in range(1, n+1)}\n        #print(A, B)\n        def find(dp, i):\n            #print(i, dp)\n            if dp[i] != i:\n                dp[i] = find(dp, dp[i])\n            return dp[i]\n        edges = sorted(edges, reverse=True)\n        \n        cnt = 0\n        for t, a, b in edges:\n            if t == 3:\n                if find(A, a) != find(A, b):\n                    A[find(A, a)] = A[find(A, b)]\n                    B[find(B, a)] = A[find(B, b)]\n                else:\n                    cnt += 1\n            elif t == 2:\n                if find(B, a) != find(B, b):\n                    B[find(B, a)] = A[find(B, b)]\n                else:\n                    cnt += 1\n            else:\n                if find(A, a) != find(A, b):\n                    A[find(A, a)] = A[find(A, b)]\n                else:\n                    cnt += 1\n        return cnt if len(set(find(A,e) for e in range(1,n+1))) == 1 and len(set(find(B,e) for e in range(1,n+1))) == 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        d1 = collections.defaultdict(lambda:[])\n        d2 = collections.defaultdict(lambda:[])\n        d3 = collections.defaultdict(lambda:[])\n        \n        count = [0]*4\n        for e in edges:\n            t = e[0]\n            u = e[1]\n            v = e[2]\n            \n            if t == 1:\n                d1[u].append(v)\n                d1[v].append(u)\n            elif t == 2:\n                d2[u].append(v)\n                d2[v].append(u)\n            else:\n                d1[u].append(v)\n                d1[v].append(u)\n                d2[u].append(v)\n                d2[v].append(u)\n                d3[u].append(v)\n                d3[v].append(u)\n            count[t] += 1\n        \n        print(count)\n        #print(d1)\n        #print(d2)\n        #print(d3)\n        \n        def check(d):\n            visited = [False]*(n+1)\n            self.sz = 0\n            def trav(i):\n                if visited[i] == True:\n                    return\n                self.sz += 1\n                visited[i] = True\n                #print(d[i])\n                for j in d[i]:\n                    trav(j)\n            trav(1)\n            #print(visited, self.sz)\n            return self.sz ==n\n        \n        if(not check(d1)):\n            return -1\n        \n        if(not check(d2)):\n            return -1\n        \n        \n        comps = 0\n        \n        visited = [False]*(n+1)\n        \n        def travers(x):\n            if visited[x] == True:\n                return\n            \n            visited[x] = True\n            for y in d3[x]:\n                travers(y)\n            \n        \n        for i in range(1, n+1):\n            if visited[i] == False:\n                comps += 1\n                travers(i)\n            #print(i, comps)\n        print(('comps', comps))\n        \n        \n        \n        print(((count[1]-comps+1) , (count[2]-comps+1), count[3]-n+comps))\n        return (count[1]-comps+1) + (count[2]-comps+1)+count[3]-n+comps\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        par = [i for i in range(n+1)]\n        wei = [1 for i in range(n+1)]\n        \n        def find(i, p):\n            if p[i] != p[p[i]]:\n                p[i] = find(p[i], p)\n            return p[i]\n        \n        def united(i, j, p):\n            return find(i, p) == find(j, p)\n        \n        def unite(i, j, p, w):\n            zi = find(i, p)\n            zj = find(j, p)\n            if w[zi] > w[zj]:\n                p[zj] = zi\n                w[zi] = w[zi] + w[zj]\n                w[zj] = 0\n            else:\n                p[zi] = zj\n                w[zj] = w[zi] + w[zj]\n                w[zi] = 0\n                \n        edges.sort()\n        ans = 0\n        for t, a, b in reversed(edges):\n            if t != 3: break\n            if united(a,b, par): ans += 1\n            else: unite(a, b, par, wei)\n                \n        p = [None, par[:], par[:] ]\n        w = [None, wei[:], wei[:] ]\n        for t, a, b in edges:\n            if t > 2: break\n            if united(a, b, p[t]): ans += 1\n            else: unite(a, b, p[t], w[t])\n        \n        for i in range(2, n+1):\n            if not united(1, i, p[1]): return -1\n            if not united(1, i, p[2]): return -1\n            \n        return ans", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        e0, e1, res = 0, 0, 0\n        self.root = [i for i in range(n+1)]\n        \n        def find(x):\n            if x == self.root[x]:\n                return x\n            self.root[x] = find(self.root[x])\n            return self.root[x]\n        \n        def isReachable(x, y):\n            x, y = find(x), find(y)\n            if x == y:\n                return True\n            self.root[x] = y\n            return False\n        \n        for t,i,j in edges:\n            if t==3:\n                if isReachable(i,j):\n                    res += 1\n                else:\n                    e0 += 1\n                    e1 += 1\n                    \n        root_cpy = self.root[:]\n        for t,i,j in edges:\n            if t==1:\n                if isReachable(i,j):\n                    res += 1\n                else:\n                    e0 += 1\n                    \n        self.root = root_cpy\n        for t,i,j in edges:\n            if t == 2:\n                if isReachable(i,j):\n                    res += 1\n                else:\n                    e1 += 1\n                    \n        return res if e0==e1==n-1 else -1\n                    \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(i):\n            if parent[i] != i: parent[i] = find(parent[i])\n            return parent[i]\n            \n        def union(x, y):\n            x, y = find(x), find(y)\n            if x == y: return True\n            parent[x] = y\n            return False\n        \n        res = a = b = 0\n        parent = list(range(n+1))\n        \n        for t, u, v in edges:\n            if t == 3:\n                if union(u, v):\n                    res += 1\n                else:\n                    a += 1\n                    b += 1\n                \n        p1 = parent[:]\n        for t, u, v in edges:\n            if t == 1:\n                if union(u, v):\n                    res += 1\n                else:\n                    a += 1\n        \n        parent = p1\n        for t, u, v in edges:\n            if t == 2:\n                if union(u, v):\n                    res += 1\n                else:\n                    b += 1\n                        \n        return res if a == b == n-1 else -1", "class UnionFindSet:\n    def __init__(self, n):\n        self.p, self.c = [i for i in range(n)], [1] * n\n    \n    def find(self, v):\n        if self.p[v] != v: self.p[v] = self.find(self.p[v])\n        return self.p[v]\n    \n    def union(self, v1, v2):\n        p1, p2 = self.find(v1), self.find(v2)\n        if p1 == p2: return False\n        if self.c[p1] < self.c[p2]: p1, p2 = p2, p1\n        self.p[p2] = p1\n        self.c[p1] += self.c[p2]\n        return True\n    \n    def count(self, v):\n        return self.c[self.find(v)]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        ret = 0\n        ufs1, ufs2 = UnionFindSet(n + 1), UnionFindSet(n + 1)\n        for t, u, v in edges:\n            if t == 3:\n                f = False\n                if ufs1.union(u, v): f = True\n                if ufs2.union(u, v): f = True\n                if f: ret += 1\n        for t, u, v in edges:\n            if t == 1 and ufs1.union(u, v): ret += 1\n            if t == 2 and ufs2.union(u, v): ret += 1\n        if ufs1.count(1) != n or ufs2.count(1) != n: return -1\n        return len(edges) - ret", "'''\nn is the number of nodes\nif 1 < value of nodes <= n\nKrustal O(ElogE)\n\nrank[node]: the longth depth of node's children\n'''\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n, edges):\n        # Union find\n        def find(node):\n            if node != parent[node]:\n                parent[node] = find(parent[node])\n            return parent[node]\n\n        def union(node1, node2):\n            parent1, parent2 = find(node1), find(node2)\n            if parent1 == parent2: return 0\n            if rank[parent1] > rank[parent2]:\n                parent[parent2] = parent1\n            elif rank[parent1] == rank[parent2]:\n                parent[parent2] = parent1\n                rank[parent1] += 1\n            else:\n                parent[parent1] = parent2 \n            \n            return 1\n\n        res = union_times_A = union_times_B = 0\n\n        # Alice and Bob\n        parent = [node for node in range(n + 1)]\n        rank = [0 for node in range(n + 1)]\n        \n        for t, node1, node2 in edges:\n            if t == 3:\n                if union(node1, node2):\n                    union_times_A += 1\n                    union_times_B += 1\n                else:\n                    res += 1\n        parent0 = parent[:]  # Alice union will change the parent array, keep origin for Bob\n\n        # only Alice\n        for t, node1, node2 in edges:\n            if t == 1:\n                if union(node1, node2):\n                    union_times_A += 1\n                else:\n                    res += 1\n\n        # only Bob\n        parent = parent0\n        for t, node1, node2 in edges:\n            if t == 2:\n                if union(node1, node2):\n                    union_times_B += 1\n                else:\n                    res += 1\n# only if Alice and Bob both union n-1 times, the graph is connected for both of them\n        return res if union_times_A == union_times_B == n - 1 else -1", "class UnionFind:\n    def __init__(self, n):\n        self.roots = [i for i in range(n)]\n        \n    def union(self, index1, index2):\n        root1 = self.find(index1)\n        root2 = self.find(index2)\n        if root1 < root2:\n            self.roots[root2] = root1\n            return 1\n        elif root1 > root2:\n            self.roots[root1] = root2\n            return 1\n        return 0\n    \n    def find(self, index):\n        if self.roots[index] != index:\n            self.roots[index] = self.find(self.roots[index])\n        return self.roots[index]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges.sort(key=lambda x: -x[0])\n        \n        uf1 = UnionFind(n)\n        uf2 = UnionFind(n)\n        could_delete = 0\n        num_union1, num_union2 = 0, 0\n        for i, edge in enumerate(edges):\n            if edge[0] == 1:\n                could_union = uf1.union(edge[1] - 1, edge[2] - 1)\n                num_union1 += could_union\n            elif edge[0] == 2:\n                could_union = uf2.union(edge[1] - 1, edge[2] - 1)\n                num_union2 += could_union\n            else:\n                could_union1 = uf1.union(edge[1] - 1, edge[2] - 1)\n                could_union2 = uf2.union(edge[1] - 1, edge[2] - 1)\n                num_union1 += could_union1\n                num_union2 += could_union2\n                could_union = could_union1 and could_union2\n            could_delete += 1 - could_union\n\n        if num_union1 != n - 1 or num_union2 != n - 1:\n            return -1\n        return could_delete\n                \n            \n", "class UnionFindSet:\n    def __init__(self, n):\n        self.parents = list(range(n))\n        self.ranks = [1] * n\n        self.size = 1\n        \n    def find(self, u):\n        if u != self.parents[u]:\n            self.parents[u] = self.find(self.parents[u])\n        return self.parents[u]\n    \n    def union(self, u, v):\n        pu, pv = self.find(u), self.find(v)\n        if pu == pv:\n            return False\n        if self.ranks[pu] > self.ranks[pv]:\n            self.parents[pv] = pu\n        elif self.ranks[pv] > self.ranks[pu]:\n            self.parents[pu] = pv\n        else:\n            self.parents[pu] = pv\n            self.ranks[pv] += 1\n        self.size += 1\n        return True\n    \n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1, uf2, ans = UnionFindSet(n), UnionFindSet(n), 0\n\\t\\t\n        for t, u, v in edges:\n            if t != 3:\n                continue\n            if not uf1.union(u - 1, v - 1) or not uf2.union(u - 1, v - 1):\n                ans += 1\n        \n        for t, u, v in edges:\n            if t == 1 and not uf1.union(u - 1, v - 1):\n                ans += 1\n            elif t == 2 and not uf2.union(u - 1, v - 1):\n                ans += 1\n   \n        return ans if uf1.size == n and uf2.size == n else -1", "from typing import *\nfrom copy import deepcopy\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges.sort(key=lambda x:(-x[0]))\n        num_used = 0\n        uf = [[i for i in range(n)]] + [[0] * n]\n        i = 0\n        while i < len(edges):\n            t, s, d = edges[i]\n            s -= 1\n            d -= 1\n            if t < 3:\n                break\n            i += 1\n            if self.union_find_merge(uf, s, d):\n                num_used += 1\n        uf1 = deepcopy(uf)\n        uf2 = deepcopy(uf)\n\n        while i < len(edges):\n            t, s, d = edges[i]\n            s -= 1\n            d -= 1\n            if t < 2:\n                break\n            i += 1\n            if self.union_find_merge(uf2, s, d):\n                num_used += 1\n\n        while i < len(edges):\n            t, s, d = edges[i]\n            s -= 1\n            d -= 1\n            i += 1\n            if self.union_find_merge(uf1, s, d):\n                num_used += 1\n\n        if self.find_num_components(n, uf1) > 1 or self.find_num_components(n, uf2) > 1:\n            return -1\n\n        return len(edges) - num_used\n\n    def find_num_components(self, n, uf):\n        num_components = 0\n        for idx in range(n):\n            parent = uf[0][idx]\n            if idx == parent:\n                num_components += 1\n        return num_components\n\n    def union_find_merge(self, uf, node1, node2):\n        p1 = self.union_find_get_parent(uf, node1)\n        p2 = self.union_find_get_parent(uf, node2)\n\n        if p1 == p2:\n            return False  # Returning false so that we don't include the s,d in result\n\n        if uf[1][p1] > uf[1][p2]:\n            uf[0][p2] = p1\n        else:\n            uf[0][p1] = p2\n            uf[1][p2] = max(uf[1][p2], uf[1][p1] + 1)\n\n        return True\n\n    def union_find_get_parent(self, uf, node):\n        while uf[0][node] != node:\n            node = uf[0][node]\n        return node", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # Union find\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n\n        res = e1 = e2 = 0\n\n        # Alice and Bob\n        root = list(range(n + 1))\n        for t, i, j in edges:\n            if t == 3:\n                if uni(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        root0 = root[:]\n\n        # only Alice\n        for t, i, j in edges:\n            if t == 1:\n                if uni(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n\n        # only Bob\n        root = root0\n        for t, i, j in edges:\n            if t == 2:\n                if uni(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if e1 == e2 == n - 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        dtype = collections.defaultdict(set)\n        \n        for e in edges:\n            dtype[(e[1], e[2])].add(e[0])\n        \n        res = 0\n        \n        for k in dtype:\n            if 3 in dtype[k]:\n                if 1 in dtype[k]:\n                    res += 1\n                    dtype[k].remove(1)\n                if 2 in dtype[k]:\n                    res += 1\n                    dtype[k].remove(2)\n        \n        da = collections.defaultdict(set)\n        db = collections.defaultdict(set)\n        \n        for k in dtype:\n            if (1 in dtype[k]) or (3 in dtype[k]):\n                da[k[0]].add(k[1])\n                da[k[1]].add(k[0])\n            if (2 in dtype[k]) or (3 in dtype[k]):\n                db[k[0]].add(k[1])\n                db[k[1]].add(k[0])\n        \n        def traversable(dd):\n            q = collections.deque([1])\n            v = set([1])\n            \n            while q:\n                node = q.popleft()\n                for nei in dd[node]:\n                    if (nei not in v):\n                        q.append(nei)\n                        v.add(nei)\n            if len(v) != n:\n                return False\n            return True\n                        \n        \n        if (not traversable(da)) or (not traversable(db)):\n            return -1\n        \n        \n        d3 = collections.defaultdict(set)\n        \n        for k in dtype:\n            if (3 in dtype[k]):\n                d3[k[0]].add(k[1])\n                d3[k[1]].add(k[0])\n        \n        def components(dd):\n            r = []\n            \n            v = set()\n            nodes = list(dd.keys())\n            lastvsize = 0\n            for node in nodes:\n                if node not in v:\n                    v.add(node)\n                    q = collections.deque([node])\n                    \n                    while q:\n                        node = q.popleft()\n                        for nei in dd[node]:\n                            if (nei not in v):\n                                q.append(nei)\n                                v.add(nei)\n                    r.append(len(v) - lastvsize)\n                    lastvsize = len(v)\n            return r\n        \n        d3ComponentSizes = components(d3)\n        need3 = 0\n        for compSize in d3ComponentSizes:\n            need3 += compSize - 1 \n        d3nodes = len(list(d3.keys()))\n        need1 = len(d3ComponentSizes) - 1 + n - d3nodes\n        need2 = len(d3ComponentSizes) - 1 + n - d3nodes\n        print(d3ComponentSizes)\n        print((len(edges), need1, need2, need3))\n        return len(edges) - (need1 + need2 + need3)\n", "class DSU:\n    def __init__(self, N):\n        self.par = list(range(N))\n        self.sz = [1] * N\n\n    def find(self, x):\n        if self.par[x] != x:\n            self.par[x] = self.find(self.par[x])\n        return self.par[x]\n\n    def union(self, x, y):\n        xr, yr = self.find(x), self.find(y)\n        if xr == yr:\n            return False\n        if self.sz[xr] < self.sz[yr]:\n            xr, yr = yr, xr\n        self.par[yr] = xr\n        self.sz[xr] += self.sz[yr]\n        self.sz[yr] = self.sz[xr]\n        return True\n\n    def size(self, x):\n        return self.sz[self.find(x)]\n\nclass Solution(object):\n    def maxNumEdgesToRemove(self, N, edges):\n        for row in edges:\n            # row[0] -= 1\n            row[1] -= 1\n            row[2] -= 1\n        alice = []\n        bob = []\n        both = []\n        \n        for t, u, v in edges:\n            if t == 1:\n                alice.append([u,v])\n            elif t==2:\n                bob.append([u,v])\n            else:\n                both.append([u,v])\n        dsu1 = DSU(N)\n        dsu2 = DSU(N)\n        ans = 0\n        for u,v  in both:\n            dsu2.union(u,v)\n            if not dsu1.union(u, v):\n                ans += 1\n        for u,v  in alice:\n            if not dsu1.union(u,v): ans += 1\n        for u,v in bob:\n            if not dsu2.union(u,v): ans += 1\n        \n        if dsu1.size(0) != N:\n            return -1\n        if dsu2.size(0) != N:\n            return -1\n        return ans", "class DisjointSet:\n    def __init__(self, number_of_sites):\n        self.parent = [i for i in range(number_of_sites+1)]\n        self.children_site_count = [1 for _ in range(number_of_sites+1)]\n        self.component_count = number_of_sites\n\n    def find_root(self, site):\n        root = site\n        while root != self.parent[root]:\n            root = self.parent[root]\n        while site != root:\n            site, self.parent[site] = self.parent[site], root\n        return root\n\n    def is_connected(self, site_1, site_2):\n        return self.find_root(site_1) == self.find_root(site_2)\n\n    def union(self, site_1, site_2):\n        site_1_root = self.find_root(site_1)\n        site_2_root = self.find_root(site_2)\n        if site_1_root == site_2_root:\n            return False\n\n        if self.children_site_count[site_1_root] < self.children_site_count[site_2_root]:\n            self.parent[site_1_root] = site_2_root\n            self.children_site_count[site_2_root] += self.children_site_count[\n                site_1_root]\n        else:\n            self.parent[site_2_root] = site_1_root\n            self.children_site_count[site_1_root] += self.children_site_count[\n                site_2_root]\n        self.component_count -= 1\n        return True\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        alice_disjoint_set = DisjointSet(n)\n        bob_disjoint_set = DisjointSet(n)\n\n        TYPE_OF_COMMON_EDGES = 3\n        TYPE_OF_ALICE_EDGES = 1\n        TYPE_OF_BOB_EDGES = 2\n\n        common_edges = filter(lambda edge: edge[0] == TYPE_OF_COMMON_EDGES, edges)\n        alice_edges = filter(lambda edge: edge[0] == TYPE_OF_ALICE_EDGES, edges)\n        bob_edges = filter(lambda edge: edge[0] == TYPE_OF_BOB_EDGES, edges)\n\n        redundant = 0\n        for _, u, v in common_edges:\n            unioned_in_alice = alice_disjoint_set.union(u, v)\n            unioned_in_bob = bob_disjoint_set.union(u, v)\n            if (not unioned_in_alice) or (not unioned_in_bob):\n                redundant += 1\n\n        for _, u, v in bob_edges:\n            if not bob_disjoint_set.union(u,v):\n                redundant += 1\n                \n        for _, u, v in alice_edges:\n            if not alice_disjoint_set.union(u, v):\n                redundant += 1\n        \n        return redundant if alice_disjoint_set.component_count == 1 and bob_disjoint_set.component_count == 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        parent = list(range(n + 1))\n        rank = [0 for _ in range(n + 1)]\n        def find(x):\n            while parent[x] != x:\n                x = parent[x]\n            return x\n        def union(x, y):\n            id_x = find(x)\n            id_y = find(y)\n            if id_x == id_y:\n                return 0\n            if rank[id_x] >= rank[id_y]:\n                if rank[id_x] == rank[id_y]:\n                    rank[id_x] += 1\n                parent[id_y] = id_x\n            else:\n                parent[id_x] = id_y\n            return 1\n        res = a = b = 0\n        for t, i, j in edges:\n            if t == 3:\n                if union(i, j):\n                    a += 1\n                    b += 1\n                else:\n                    res += 1\n        parent_, rank_ = parent[:], rank[:]\n        for t, i, j in edges:\n            if t == 1:\n                if union(i, j):\n                    a += 1\n                else:\n                    res += 1\n        parent, rank = parent_, rank_\n        for t, i, j in edges:\n            if t == 2:\n                if union(i, j):\n                    b += 1\n                else:\n                    res += 1\n        return res if (a == n - 1 and b == n - 1) else -1\n\n", "class DAU():\n    def __init__(self,n):\n        self.parent = list(range(n ))\n        \n    def find(self,p):\n        if self.parent[p] != p:\n            self.parent[p] = self.find(self.parent[p])\n        return self.parent[p]\n    def union(self,p,q):\n        pr, qr = self.find(p), self.find(q)\n        if pr == qr:\n            return False\n        else:\n            self.parent[pr] = qr\n            return True\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        ufA, ufB, ufAB = DAU(n), DAU(n), DAU(n)\n        usefulAB = 0\n        for edge in edges:\n            t = edge[0]\n            x = edge[1]\n            y = edge[2]\n            if t == 1:\n                ufA.union(x-1, y - 1)\n            elif t == 2:\n                ufB.union(x-1, y - 1)\n            else:\n                ufA.union(x-1, y - 1)\n                ufB.union(x-1, y - 1)\n                usefulAB += ufAB.union(x-1, y - 1)\n        if len([i for i in range(n) if ufA.parent[i] == i]) > 1 or len([i for i in range(n) if ufB.parent[i] == i]) > 1 :\n            return -1\n        return len(edges) - (2*(n - 1) - usefulAB)\n                \n            \n", "class DSU:\n    def __init__(self, N):\n        self.par = list(range(N))\n        self.sz = [1] * N\n\n    def find(self, x):\n        if self.par[x] != x:\n            self.par[x] = self.find(self.par[x])\n        return self.par[x]\n\n    def union(self, x, y):\n        xr, yr = self.find(x), self.find(y)\n        if xr == yr:\n            return False\n        if self.sz[xr] < self.sz[yr]:\n            xr, yr = yr, xr\n        self.par[yr] = xr\n        self.sz[xr] += self.sz[yr]\n        self.sz[yr] = self.sz[xr]\n        return True\n\n    def size(self, x):\n        return self.sz[self.find(x)]\n    \n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        edges.sort(reverse=True)\n        \n        bob = DSU(n)\n        alice = DSU(n)\n        \n        res = 0\n        \n        for t, a, b in edges:\n            a -= 1\n            b -= 1\n            if t == 3 and (bob.find(a) != bob.find(b) or alice.find(a) != alice.find(b)):\n                bob.union(a, b)\n                alice.union(a, b)\n            elif t == 2 and bob.find(a) != bob.find(b):\n                bob.union(a, b)\n            elif t == 1 and alice.find(a) != alice.find(b):\n                alice.union(a, b)\n            else:\n                res += 1\n        \n        is_one = lambda dsu: len({dsu.find(i) for i in range(n)}) == 1\n        \n        return res if is_one(alice) and is_one(bob) else -1\n", "from collections import defaultdict\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:    \n        # 1\u3001\u5173\u4e8e\u4e24\u4e2a\u4eba\u7684\u8fde\u901a\u6027\u8003\u5bdf\uff0c O\uff08N\uff09\n        # 2\u3001\u5f00\u59cb\u79fb\u9664 \u6709type3\u548c\u5176\u4ed6\u7684\u8fb9\u91cd\u590d\uff0c\u4f18\u5148\u79fb\u9664\u5176\u4ed6\u7684\n        # 3\u3001\u53bb\u6389type3\u7ec4\u6210\u7684\u73af\uff08\u7528\u8282\u70b9\u6570\u548c\u8fb9\u7684\u6570\u91cf\u5173\u7cfb\u6765\u8ba1\u7b97\u9700\u8981\u53bb\u9664\u591a\u5c11\u4e2a\u591a\u4f59\u7684\u8fb9\uff09\n        # 4\u3001\u7136\u540e\u5355\u72ec\u8003\u5bdf\u6bcf\u4e2a\u4eba\u7684\u91cd\u590d\u8fb9\uff08\u7528\u8fb9\u548c\u8282\u70b9\u6570\u91cf\u5173\u7cfb\u8ba1\u7b97\uff09\n        count = 0\n        type3 = set()\n        adj3 = defaultdict(list)\n        for edge in edges:\n            edge_type, node1, node2 = edge\n            if edge_type==3:\n                type3.add((node1,node2))  \n                adj3[node1].append(node2)\n                adj3[node2].append(node1)\n        \n        type1,type2 = set(),set()\n        adj1,adj2 = defaultdict(list),defaultdict(list)\n        for edge in edges:\n            edge_type, node1, node2 = edge\n            if edge_type==3:\n                continue\n            if (node1,node2) in type3 or (node2,node1) in type3:\n                count += 1\n                continue\n            if edge_type==1:\n                type1.add((node1,node2))\n                adj1[node1].append(node2)\n                adj1[node2].append(node1)\n            elif edge_type==2:\n                type2.add((node1,node2))\n                adj2[node1].append(node2)\n                adj2[node2].append(node1)\n        \n        # \u8fde\u901a\u6027\u8003\u5bdf\n        visited = set(range(1,n+1))\n        queue = [1]\n        while queue and visited:\n            new_queue = []\n            while queue:\n                curr = queue.pop()\n                if curr in visited:\n                    visited.remove(curr)\n                    for node in adj1[curr]+adj3[curr]:\n                        if node in visited:\n                            new_queue.append(node)\n            queue = new_queue\n        if visited: # \u8bf4\u660e\u4e0d\u80fd\u904d\u5386\n            return -1\n        visited = set(range(1,n+1))\n        queue = [1]\n        while queue and visited:\n            new_queue = []\n            while queue:\n                curr = queue.pop()\n                if curr in visited:\n                    visited.remove(curr)\n                    for node in adj2[curr]+adj3[curr]:\n                        if node in visited:\n                            new_queue.append(node)\n            queue = new_queue\n        if visited: # \u8bf4\u660e\u4e0d\u80fd\u904d\u5386\n            return -1\n        \n        # type3, adj3\n        # \u9700\u8981\u5728\u641c\u7d22\u8fc7\u7a0b\u4e2d\u540c\u65f6\u8bb0\u5f55\u8fb9\u548c\u8282\u70b9\n        used = set()\n        for node1,node2 in type3:\n            if node1 in used:\n                continue\n            edge_record = set()\n            node_record = set()\n            queue = [node1]\n            while queue:\n                new_queue = []\n                while queue:\n                    curr = queue.pop()\n                    used.add(curr)\n                    node_record.add(curr)\n                    for node in adj3[curr]:\n                        if node in used:\n                            continue\n                        if (curr,node) in type3:\n                            edge_record.add((curr,node))\n                        else:\n                            edge_record.add((node, curr))\n                        new_queue.append(node)\n                queue = new_queue\n            count -= len(edge_record) - len(node_record) + 1\n     \n        \n        # \u53bb\u9664type3\u7684\u73af\n        return count + len(type1)+len(type3) - n +1 + len(type2)+len(type3)-n+1\n        \n            \n        \n                    \n            \n        \n", "class UnionFind:\n    # When n is valid, each element is a tuple of two integers, (x, y)\n    def __init__(self, m: int, n: int = None):\n        self.rank = collections.Counter()\n        if n is None:\n            self.parent = [i for i in range(m)]\n        else:\n            self.parent = {(i, j): (i, j) for i in range(m) for j in range(n)}\n\n    def find(self, x):\n        if self.parent[x] != x:\n            self.parent[x] = self.find(self.parent[x])\n        return self.parent[x]\n\n    def union(self, x, y):\n        px = self.find(x)\n        py = self.find(y)\n        if px == py:\n            return\n        if self.rank[py] > self.rank[px]:\n            px, py = py, px\n        if self.rank[py] == self.rank[px]:\n            self.rank[px] += 1\n        self.parent[py] = px\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1 = UnionFind(n)\n        uf2 = UnionFind(n)\n        res = 0\n        e1 = e2 = e3 = 0\n        for e in edges:\n            t, u, v = e[0], e[1] - 1, e[2] - 1\n            if t == 3:\n                if uf1.find(u) == uf1.find(v):\n                    res += 1\n                else:\n                    uf1.union(u, v)\n                    uf2.union(u, v)\n                    e3 += 1\n        for e in edges:\n            t, u, v = e[0], e[1] - 1, e[2] - 1\n            if t == 1:\n                if uf1.find(u) == uf1.find(v):\n                    res += 1\n                else:\n                    uf1.union(u, v)\n                    e1 += 1\n            elif t == 2:\n                if uf2.find(u) == uf2.find(v):\n                    res += 1\n                else:\n                    uf2.union(u, v)\n                    e2 += 1\n        total = (n - 1) * 2\n        if e3 * 2 + e1 + e2 < total:\n            return -1\n        return res\n", "class UnionFindSet:\n    def __init__(self, n):\n        self.parents = list(range(n))\n        self.ranks = [1] * n\n        self.size = 1\n        \n    def find(self, u):\n        if u != self.parents[u]:\n            self.parents[u] = self.find(self.parents[u])\n        return self.parents[u]\n    \n    def union(self, u, v):\n        pu, pv = self.find(u), self.find(v)\n        if pu == pv:\n            return False\n        if self.ranks[pu] > self.ranks[pv]:\n            self.parents[pv] = pu\n        elif self.ranks[pv] > self.ranks[pu]:\n            self.parents[pu] = pv\n        else:\n            self.parents[pu] = pv\n            self.ranks[pv] += 1\n        self.size += 1\n        return True\n    \n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1, uf2 = UnionFindSet(n), UnionFindSet(n)\n        ans = 0\n        for t, u, v in edges:\n            if t != 3:\n                continue\n            if not uf1.union(u - 1, v - 1) or not uf2.union(u - 1, v - 1):\n                ans += 1\n        \n        for t, u, v in edges:\n            if t == 1 and not uf1.union(u - 1, v - 1):\n                ans += 1\n            elif t == 2 and not uf2.union(u - 1, v - 1):\n                ans += 1\n   \n        print(uf1.size, uf2.size)\n        return ans if uf1.size == n and uf2.size == n else -1", "\nclass DSU:\n    \n    def __init__(self, a):\n        self.par = {x:x for x in a}\n    \n    def merge(self, u, v):\n        rootu = self.find(u)\n        rootv = self.find(v)\n        \n        if rootu == rootv:\n            return False\n        \n        self.par[rootu] = rootv\n        return True\n    \n    def find(self, u):\n        if self.par[u] != u:\n            self.par[u] = self.find(self.par[u])\n        return self.par[u]\n    \n    def roots(self):\n        return set(self.find(u) for u in self.par)\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        dsu1 = DSU(range(1,n+1))\n        dsu2 = DSU(range(1,n+1))\n        \n        grouper = defaultdict(list)\n        for t,u,v in edges:\n            grouper[t].append([u,v])\n        \n        both, alice, bob = grouper[3], grouper[1], grouper[2]\n        \n        ret = 0\n        \n        for u,v in both:\n            if not dsu1.merge(u, v):\n                ret += 1\n            dsu2.merge(u, v)\n                \n        for u,v in alice:\n            if not dsu1.merge(u, v):\n                ret += 1\n        \n        for u,v in bob:\n            if not dsu2.merge(u, v):\n                ret += 1\n        \n        if len(dsu1.roots()) != 1 or len(dsu2.roots()) != 1:\n            return -1\n            \n        return ret", "class DisjointSet:\n    def __init__(self, n):\n        self._parent = [i for i in range(n)]\n        self._count = [1 for _ in range(n)]\n    \n    def parent(self, i):\n        p = i\n        while self._parent[p] != p:\n            p = self._parent[p]\n        self._parent[i] = p\n        return p\n    \n    def count(self, i):\n        return self._count[self.parent(i)]\n    \n    def merge(self, i, j):\n        pi = self.parent(i)\n        pj = self.parent(j)\n        \n        if pi == pj:\n            return False\n        ci, cj = self._count[pi], self._count[pj]\n        if ci <= cj:\n            self._parent[j] = self._parent[pj] = pi\n            self._count[pi] += self._count[pj]\n            self._count[pj] = 0\n        else:\n            self._parent[i] = self._parent[pi] = pj\n            self._count[pj] += self._count[pi]\n            self._count[pi] = 0\n        return True\n            \n    def clone(self):\n        other = DisjointSet(len(self._parent))\n        other._parent = [p for p in self._parent]\n        other._count = [c for c in self._count]\n        return other\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges_deleted = 0\n        dsA = DisjointSet(n)\n        for edge_type, u, v in edges:\n            ui, vi = u-1, v-1\n            if edge_type == 3:\n                if not dsA.merge(ui, vi):\n                    edges_deleted += 1\n        \n        dsB = dsA.clone()\n        \n        for edge_type, u, v in edges:\n            ui, vi = u-1, v-1\n            if edge_type == 1:\n                if not dsA.merge(ui, vi):\n                    edges_deleted += 1\n                    \n        if sum(c > 0 for c in dsA._count) != 1:\n            return -1\n\n        for edge_type, u, v in edges:\n            ui, vi = u-1, v-1\n            if edge_type == 2:\n                if not dsB.merge(ui, vi):\n                    edges_deleted += 1\n\n        if sum(c > 0 for c in dsB._count) != 1:\n            return -1\n                    \n        return edges_deleted", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        a_uf = UF(n+1)\n        b_uf = UF(n+1)\n        unwanted = 0\n\n        for t, u, v in edges:    \n            if t == 3:            \n                # Alice and Bob\n                if a_uf.find(u) == a_uf.find(v) and b_uf.find(u) == b_uf.find(v):\n                    # both guys dont need\n                    unwanted += 1\n                else:\n                    a_uf.union(u, v)\n                    b_uf.union(u, v)\n        \n        for t, u, v in edges:\n            #print((t, u, v))\n            if t == 1:\n                # Alice\n                if a_uf.find(u) == a_uf.find(v):\n                    # dont need this\n                    unwanted += 1\n                else:\n                    a_uf.union(u, v)\n                    \n        if a_uf.size[a_uf.find(1)] < n:\n            return -1\n\n        for t, u, v in edges:    \n            if t == 2:\n                # Bob\n                if b_uf.find(u) == b_uf.find(v):\n                    # dont need this\n                    unwanted += 1\n                else:\n                    b_uf.union(u, v)\n                \n        if b_uf.size[b_uf.find(1)] < n:\n            return -1\n                \n        return unwanted\n    \nclass UF:\n    def __init__(self, n):\n        self.uf = [i for i in range(n)]\n        self.size = [1] * n\n    \n    def find(self, u):\n        while u != self.uf[u]:\n            self.uf[u] = self.uf[self.uf[u]]\n            u = self.uf[u]\n        return u\n    \n    def union(self, u, v):\n        rootU = self.find(u)\n        rootV = self.find(v)\n        if rootU != rootV:\n            if self.size[rootU] > self.size[rootV]:\n                self.size[rootU] += self.size[rootV]\n                self.uf[rootV] = rootU\n            else:\n                self.size[rootV] += self.size[rootU]\n                self.uf[rootU] = rootV\n        \n        \n", "class UnionFind():\n    def __init__(self):\n        self.uf, self.rank, self.size = {}, {}, {}\n        self.roots = set()\n        \n    def add(self, x):\n        if x not in self.uf:\n            self.uf[x], self.rank[x], self.size[x] = x, 0, 1\n            self.roots.add(x)\n        \n    def find(self, x):\n        self.add(x)\n        if x != self.uf[x]:\n            self.uf[x] = self.find(self.uf[x])\n        return self.uf[x]\n\n    def union(self, x, y):  \n        xr, yr = self.find(x), self.find(y)\n        if xr == yr: return\n        if self.rank[xr] <= self.rank[yr]:\n            self.uf[xr] = yr\n            self.size[yr] += self.size[xr]\n            self.rank[yr] += (self.rank[xr] == self.rank[yr])\n            self.roots.discard(xr)\n        else:\n            self.uf[yr] = xr\n            self.size[xr] += self.size[yr]\n            self.roots.discard(yr)\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        Ga, Gb, Gab = UnionFind(), UnionFind(), UnionFind()\n        for x in range(1, n + 1):\n            Ga.add(x), Gb.add(x), Gab.add(x)\n        for t, x, y in edges:\n            if t in (1, 3): Ga.union(x, y)\n            if t in (2, 3): Gb.union(x, y)\n            if t == 3: Gab.union(x,y)\n        \n        if max(len(Ga.roots), len(Gb.roots)) > 1: return -1\n        c = len(Gab.roots)\n        return len(edges) - (n - c + 2 * (c - 1))", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edge_set = set(tuple(x) for x in edges)\n        \n        graphs = [defaultdict(list) for i in range(3)]\n        for e in edges:\n            graphs[e[0]-1][e[1]].append(e[2])\n            graphs[e[0]-1][e[2]].append(e[1])\n            if e[0] == 3:\n                for k in [0, 1]:\n                    graphs[k][e[1]].append(e[2])\n                    graphs[k][e[2]].append(e[1])\n\n        def ct_ccmp(g):\n            visited = dict()\n            q = deque()\n            nt = 0\n            for i in range(1, n + 1):\n                if i in visited:\n                    continue\n                if len(g[i]) > 0:\n                    q.append(i)\n                    visited[i] = nt\n                    while len(q) > 0:\n                        cur = q.popleft()\n                        for x in g[cur]:\n                            if x in visited:\n                                continue\n                            q.append(x)\n                            visited[x] = nt\n                nt += 1\n            return nt\n    \n        if ct_ccmp(graphs[0]) > 1 or ct_ccmp(graphs[1]) > 1:\n            return -1\n        \n        nt = ct_ccmp(graphs[2])\n        return len(edges) - (n - nt) - 2 * (nt - 1)\n                \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n\n        def find(comp_id):\n            while parents[comp_id] != comp_id:\n                parents[comp_id] = parents[parents[comp_id]]\n                comp_id = parents[comp_id]\n            return comp_id\n\n        def union(id_1, id_2):\n            px = find(id_1)\n            py = find(id_2)\n            parents[py] = px\n            return px != py\n\n        # use union find for components\n        # init each node as a component\n        # once an edge is found (use num 3 first) then\n        # union the components, if there is change, then amount of components change\n        # if only one component left then the graph is traversable\n        parents = [idx for idx in range(n)]\n        removed_edges = 0\n        for link in edges:\n            if link[0] != 3:\n                continue\n            # only double linked edges first\n            if not union(link[1] - 1, link[2] - 1):\n                removed_edges += 1\n\n        bob_parents = parents[:]\n        for link in edges:\n            if link[0] != 1:\n                continue\n            if not union(link[1] - 1, link[2] - 1):\n                removed_edges += 1\n        if len(Counter(find(i) for i in parents)) != 1:  # LLRN - use find() instead of referring directly\n            return -1\n\n        parents = bob_parents\n        for link in edges:\n            if link[0] != 2:\n                continue\n            if not union(link[1] - 1, link[2] - 1):\n                removed_edges += 1\n        if len(Counter(find(i) for i in parents)) != 1:\n            return -1\n        return removed_edges", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        ufAli = uf(n)\n        ufBob = uf(n)\n        \n#         10 -> 2\n        for edg in edges:\n            x, y = edg[1], edg[2]\n            if edg[0] == 1:\n                ufAli.addEdge(x, y, 10)\n            elif edg[0] == 2:\n                ufBob.addEdge(x, y, 10)\n            else:\n                ufAli.addEdge(x, y, 1)\n                ufBob.addEdge(x, y, 1)\n                \n        # print(ufAli.g, ufAli.kruskalmst())\n        # print(ufBob.g, ufBob.kruskalmst())\n        \n        blueremoved = set()\n        aliremoved = set()\n        bobremoved = set()\n        \n        ans1 = ufAli.kruskalmst(blueremoved, aliremoved)\n        ans2 = ufBob.kruskalmst(blueremoved, bobremoved)\n        if ans1 == -1 or ans2 == -1:\n            return -1\n        \n        # return ans1 + ans2\n        return len(blueremoved) + len(aliremoved) + len(bobremoved)\n        \n        \n                \n        \n        \n        \n\nclass uf:\n    def __init__(self, n):\n        self.n = n\n        self.g = []\n        self.joinednodes = set()\n        # self.totalnodes = set()\n        \n        \n    def addEdge(self, x, y, cost):\n        self.g.append((x, y, cost))\n        # self.joinednodes \n        \n    def find(self, x, parent):\n        if parent[x] == x:\n            return x\n        \n        return self.find(parent[x], parent)\n    \n    def union(self, x, y, parent, rank):\n        xroot, yroot = self.find(x, parent), self.find(y, parent)\n        \n        if xroot != yroot:\n            if rank[xroot] > rank[yroot]:\n                parent[yroot] = xroot\n            elif rank[yroot] > rank[xroot]:\n                parent[xroot] = yroot\n            else:\n                parent[yroot] = xroot\n                rank[xroot] += 1\n                \n    def kruskalmst(self, blue, rorg):\n        # parent = { for edge in g}\n        parent = {}\n        rank = {}\n        for edge in self.g:\n            parent[edge[0]] = edge[0]\n            parent[edge[1]] = edge[1]\n            rank[edge[0]] = 0\n            rank[edge[1]] = 0\n            \n        # print(parent, rank)\n        success = 0\n        self.g.sort(key=lambda edge: edge[2])\n        for edge in self.g:\n            x, y, cos = edge\n            xroot = self.find(x, parent)\n            yroot = self.find(y, parent)\n            if xroot != yroot:\n                success += 1\n                self.union(xroot, yroot, parent, rank)\n                \n            else:\n                if cos == 1:\n                    blue.add((x,y))\n                else:\n                    rorg.add((x,y))\n                \n                \n        \n                \n        if success == self.n -1:\n            \n            # return success\n            return len(self.g) - success\n        \n        return -1\n            \n            \n            \n            \n            \n        \n                \n                \n        \n        \n", "from collections import defaultdict, deque\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # 1\u3001\u79fb\u9664type3\u7684\u91cd\u590d\u8fb9\n        # 2\u3001\u68c0\u67e5alice\u548cbob\u7684\u53ef\u904d\u5386\u6027\n        # 3\u3001\u68c0\u67e5type3\u7ec4\u6210\u7684\u73af\n        # 4\u3001\u68c0\u67e5alice type1\u7ec4\u6210\u7684\u73af\n        # 5\u3001\u68c0\u67e5bob type2\u7ec4\u6210\u7684\u73af\n        count = 0\n        set1, set2, set3 = set(), set(), set()\n        adj_a, adj_b, adj_3 = defaultdict(set), defaultdict(set), defaultdict(set)\n        for edge in edges:\n            tp, i, j = edge\n            if tp == 3:\n                set3.add((i,j))\n                adj_3[i].add(j)\n                adj_3[j].add(i)\n                adj_a[i].add(j)\n                adj_a[j].add(i)\n                adj_b[i].add(j)\n                adj_b[j].add(i)\n        \n        for edge in edges:\n            tp, i, j = edge\n            if tp != 3:\n                if ((i,j) in set3 or (j,i) in set3):\n                    count += 1\n                elif tp == 1:\n                    set1.add((i,j))\n                    adj_a[i].add(j)\n                    adj_a[j].add(i)\n                elif tp == 2:\n                    set2.add((i,j))\n                    adj_b[i].add(j)\n                    adj_b[j].add(i)\n                    \n        def is_traversable(adj):\n            visited = set()\n            queue = deque([1])\n            while queue:\n                root = queue.popleft()\n                visited.add(root)\n                for i in adj[root]:\n                    if i not in visited:\n                        queue.append(i)\n            if len(visited) != n:\n                return False\n            else:\n                return True\n            \n        \n        if not is_traversable(adj_a) or not is_traversable(adj_b):\n            return -1\n        \n        dup_3 = 0\n        visited = set()\n        for edge in set3:\n            if edge in visited:\n                continue\n            node_set = set()\n            edge_set = set()\n            i, j = edge\n            queue = deque([i])\n            while queue:\n                root = queue.popleft()\n                node_set.add(root)\n                for k in adj_3[root]:\n                    if k not in node_set:\n                        queue.append(k)\n                        if (root, k) in set3:\n                            edge_set.add((root, k))\n                        else:\n                            edge_set.add((k, root))\n                            \n            dup_3 += len(edge_set) - len(node_set) + 1\n            for v_edge in edge_set:\n                visited.add(v_edge)\n                \n        type3_count = len(set3) - dup_3        \n        return count + len(set1) + 2 * type3_count - n + 1 + len(set2) - n + 1 + dup_3\n\n        \n        \n            \n        \n        \n                \n            \n                    \n            \n", "class UnionFind:\n    def __init__(self,N):\n        self.par = [-1]*N\n        self.N = N\n    \n    def find(self,x):\n        if self.par[x] < 0:\n            return x\n        else:\n            self.par[x] = self.find(self.par[x])\n            return self.par[x]\n        \n    def union(self,x,y):\n        x = self.find(x)\n        y = self.find(y)\n        if x == y:\n            return\n        if self.par[x] > self.par[y]:\n            x,y = y,x\n        \n        self.par[x] += self.par[y]\n        self.par[y] = x\n        \n    def roots(self):\n        return [i for i, x in enumerate(self.par) if x < 0]\n\n    def groupCount(self):\n        return len(self.roots())\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf = UnionFind(n)\n        uf1 = UnionFind(n)\n        uf2 = UnionFind(n)\n        ans = 0\n        m = len(edges)\n        for edge in edges:\n            if edge[0] == 3:\n                if uf.find(edge[1]-1) == uf.find(edge[2]-1):\n                    ans += 1\n                uf.union(edge[1]-1,edge[2]-1)\n                uf1.union(edge[1]-1,edge[2]-1)\n                uf2.union(edge[1]-1,edge[2]-1)\n            elif edge[0] == 1:\n                uf1.union(edge[1]-1,edge[2]-1)\n                ans += 1\n            else:\n                uf2.union(edge[1]-1,edge[2]-1)\n                ans += 1\n        connected = uf.groupCount()\n        removable = ans + 2 - 2*connected\n        if max(uf1.groupCount(),uf2.groupCount()) > 1 or removable < 0:\n            return -1\n        if connected == 1:\n            return ans\n        else:\n            return removable\n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        parenta = [x for x in range(n+1)]\n        parentb = [x for x in range(n+1)]\n        \n        def ufind(parent, x):\n            if parent[x] != x:\n                parent[x] = ufind(parent, parent[x])\n            return parent[x]\n        \n        def uunion(parent, a, b):\n            ua = ufind(parent, a)\n            ub = ufind(parent, b)\n            \n            parent[ua] = ub\n            \n        edges.sort(key=lambda x: (-x[0]))\n        \n        count = 0\n        for t, u, v in edges:\n            if t == 3:\n                if ufind(parenta, u) != ufind(parenta, v) or ufind(parentb, u) != ufind(parentb, v):\n                    uunion(parenta, u, v)\n                    uunion(parentb, u, v)\n                else:\n                    count += 1\n            elif t == 2:\n                if ufind(parentb, u) != ufind(parentb, v):\n                    uunion(parentb, u, v)\n                else:\n                    count += 1\n            else:\n                if ufind(parenta, u) != ufind(parenta, v):\n                    uunion(parenta, u, v)\n                else:\n                    count += 1\n            \n        roota = ufind(parenta, 1)\n        rootb = ufind(parentb, 1)\n        for x in range(1, n+1):\n            if ufind(parenta, x) != roota or ufind(parentb, x) != rootb:\n                return -1\n            \n        return count", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        self.parent = [i for i in range(n+1)]\n        res=e1=e2=0\n        for t,i,j in edges:\n            if t==3:\n                if self.union(i,j):\n                    e1+=1\n                    e2+=1\n                else:\n                    res+=1\n        self.parent0 = self.parent[:]\n        for t,i,j in edges:\n            if t==1:\n                if self.union(i,j):\n                    e1+=1\n                else:\n                    res+=1\n        self.parent = self.parent0\n        for t,i,j in edges:\n            if t==2:\n                if self.union(i,j):\n                    e2+=1\n                else:\n                    res+=1\n        return res if e1==e2==n-1 else -1\n    def find(self,i):\n        if i!=self.parent[i]:\n            self.parent[i]=self.find(self.parent[i])\n        return self.parent[i]\n    def union(self,x,y):\n        x,y = self.find(x),self.find(y)\n        if x==y:\n            return 0\n        self.parent[x]=y\n        return 1\n    \n", "class DSU:\n    def __init__(self, N):\n        self.par = list(range(N))\n        self.sz = [1] * N\n\n    def find(self, x):\n        if self.par[x] != x:\n            self.par[x] = self.find(self.par[x])\n        return self.par[x]\n\n    def union(self, x, y):\n        xr, yr = self.find(x), self.find(y)\n        if xr == yr:\n            return False\n        if self.sz[xr] < self.sz[yr]:\n            xr, yr = yr, xr\n        self.par[yr] = xr\n        self.sz[xr] += self.sz[yr]\n        self.sz[yr] = self.sz[xr]\n        return True\n\n    def size(self, x):\n        return self.sz[self.find(x)]\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        '''\n        for vertex\n            if has type3 edge remove all other edges\n            else:\n                 \n        '''\n        dsuA = DSU(n+1)\n        dsuB = DSU(n+1)\n        \n        ans = 0\n        for t, u, v in edges:\n            if t == 3:\n                if not dsuA.union(u, v):\n                    ans += 1\n                dsuB.union(u, v)\n        for t, u, v in edges:\n            if t == 1:\n                if not dsuA.union(u, v):\n                    ans += 1\n            if t == 2:\n                if not dsuB.union(u, v):\n                    ans += 1\n        return ans if dsuA.size(1) == dsuB.size(1) == n else -1\n#         hasType3 = [False for _ in range(n)]\n#         graph = collections.defaultdict(list)\n#         count = 0\n#         for a, b, c in edges:\n#             if a == 3:\n#                 hasType3[b-1] = True\n#                 hasType3[c-1] = True\n#             graph[b].append([c, a])\n#             graph[c].append([b, a])\n#         seenA = [False for i in range(n)]\n#         seenB = [False for i in range(n)]\n#         def dfs(node, ty):\n#             for nei, t in graph[node]:\n#                 if ty == 1:\n#                     if (t == 1 or t == 3) and not seenA[nei-1]:\n#                         seenA[nei-1] = True\n#                         dfs(nei, ty)\n#                 if ty == 2:\n#                     if (t == 2 or t == 3) and not seenB[nei-1]:\n#                         seenB[nei-1] = True\n#                         dfs(nei, ty)\n#         dfs(edges[0][1], 1)\n#         dfs(edges[0][1], 2)\n#         seenA[edges[0][1]-1] = True\n#         seenB[edges[0][1]-1] = True\n#         # print(seenA, seenB)\n#         if not all(seenA) or not all(seenB):\n#             return -1\n#         ans = 0\n#         for i, a in enumerate(hasType3):\n#             if not a:\n#                 ans += 2\n#             else:\n#                 ans += 1\n\n#         return max(len(edges) - ans+1, 0)\n", "class DSU:\n    def __init__(self, N):\n        self.par = list(range(N))\n        self.sz = [1] * N\n\n    def find(self, x):\n        if self.par[x] != x:\n            self.par[x] = self.find(self.par[x])\n        return self.par[x]\n\n    def union(self, x, y):\n        xr, yr = self.find(x), self.find(y)\n        if xr == yr:\n            return False\n        if self.sz[xr] < self.sz[yr]:\n            xr, yr = yr, xr\n        self.par[yr] = xr\n        self.sz[xr] += self.sz[yr]\n        self.sz[yr] = self.sz[xr]\n        return True\n\n    def size(self, x):\n        return self.sz[self.find(x)]\n\nclass Solution(object):\n    def maxNumEdgesToRemove(self, N, edges):\n        for row in edges:\n            # row[0] -= 1\n            row[1] -= 1\n            row[2] -= 1\n        alice = []\n        bob = []\n        both = []\n        \n        for t, u, v in edges:\n            if t == 1:\n                alice.append([u,v])\n            elif t==2:\n                bob.append([u,v])\n            else:\n                both.append([u,v])\n        dsu1 = DSU(N)\n        dsu2 = DSU(N)\n        ans = 0\n        for u,v  in both:\n            dsu2.union(u,v)\n            if not dsu1.union(u, v):\n                ans += 1\n        for u,v  in alice:\n            if not dsu1.union(u,v): ans += 1\n        for u,v in bob:\n            if not dsu2.union(u,v): ans += 1\n        \n        if dsu1.size(0) != N:\n            return -1\n        if dsu2.size(0) != N:\n            return -1\n        return ans\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges.sort(reverse=True)\n        a_uf = UF(n+1)\n        b_uf = UF(n+1)\n        unwanted = 0\n        \n        for t, u, v in edges:\n            #print((t, u, v))\n            if t == 1:\n                # Alice\n                if a_uf.find(u) == a_uf.find(v):\n                    # dont need this\n                    unwanted += 1\n                else:\n                    a_uf.union(u, v)\n            elif t == 2:\n                # Bob\n                if b_uf.find(u) == b_uf.find(v):\n                    # dont need this\n                    unwanted += 1\n                else:\n                    b_uf.union(u, v)\n            else:\n                # Alice and Bob\n                if a_uf.find(u) == a_uf.find(v) and b_uf.find(u) == b_uf.find(v):\n                    # both guys dont need\n                    unwanted += 1\n                else:\n                    a_uf.union(u, v)\n                    b_uf.union(u, v)\n                \n        if a_uf.size[a_uf.find(1)] < n or b_uf.size[b_uf.find(1)] < n:\n            return -1\n                \n        return unwanted\n    \nclass UF:\n    def __init__(self, n):\n        self.uf = [i for i in range(n)]\n        self.size = [1] * n\n    \n    def find(self, u):\n        while u != self.uf[u]:\n            self.uf[u] = self.uf[self.uf[u]]\n            u = self.uf[u]\n        return u\n    \n    def union(self, u, v):\n        rootU = self.find(u)\n        rootV = self.find(v)\n        if rootU != rootV:\n            if self.size[rootU] > self.size[rootV]:\n                self.size[rootU] += self.size[rootV]\n                self.uf[rootV] = rootU\n            else:\n                self.size[rootV] += self.size[rootU]\n                self.uf[rootU] = rootV\n        \n        \n", "import copy\n\nclass Solution:\n    \n    def findParent(self, v, parent):\n            if parent[v] == -1:\n                return v\n            else:\n                return self.findParent(parent[v], parent)\n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        bothEdges = []\n        aEdges = []\n        bEdges = []\n        \n        aNodes = set()\n        bNodes = set()\n        \n        for e in edges:\n            if e[0] == 3:\n                bothEdges.append(e)\n                aNodes.add(e[2])\n                aNodes.add(e[1])\n                bNodes.add(e[2])\n                bNodes.add(e[1])\n            elif e[0] == 1:\n                aEdges.append(e)\n                aNodes.add(e[2])\n                aNodes.add(e[1])\n            else:\n                bEdges.append(e)\n                bNodes.add(e[2])\n                bNodes.add(e[1])\n                \n        if len(aNodes) < n or len(bNodes) < n:\n            return -1\n        \n        parents = [-1 for _ in range(n + 1)]\n\n        mstCommon = 0\n        for e in bothEdges:\n            x, y = e[1], e[2]\n            xp = self.findParent(x, parents)\n            yp = self.findParent(y, parents)\n            if xp == yp:\n                continue\n            else:\n                parents[xp] = yp\n                mstCommon += 1\n                if mstCommon == n - 1:\n                    break\n        \n        if mstCommon == n - 1:\n            return len(edges) - (n - 1)\n        else:\n            return len(edges) - (n - 1) - (n - 1) + mstCommon", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges) -> int:\n        def find(i):\n            if root[i] != i:\n                root[i] = find(root[i])\n            return root[i]\n\n        def union(u, v):\n            ru, rv = find(u), find(v)\n            if ru == rv:\n                return False\n            root[ru] = rv\n            return True\n\n        root = list(range(n+1))\n        edges_alice, edges_bob, res = 0, 0, 0\n        for t, u, v in edges:\n            if t == 3:\n                if union(u, v):\n                    edges_alice += 1\n                    edges_bob += 1\n                else:\n                    res += 1\n\n        root_copy = root[:] # a copy of connection 3\n        for t, u, v in edges:\n            if t == 1:\n                if union(u, v):\n                    edges_alice += 1\n                else:\n                    res +=1\n\n        root = root_copy\n        for t, u, v in edges:\n            if t == 2:\n                if union(u, v):\n                    edges_bob += 1\n                else:\n                    res += 1\n        if edges_alice == n-1 and edges_bob == n-1:\n            return res\n        else:\n            return -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n\n        def find(x,y,d):\n            nonlocal res\n            count = 0\n            arr = []\n            for key,val in d.items():\n                if x in val and y in val:\n                    res += 1\n                    count = -1\n                    break\n                elif x in val or y in val:\n                    d[key].add(x)\n                    d[key].add(y)\n                    count += 1\n                    arr.append(key)\n            \n            if count == -1:\n                pass\n            elif count == 0:\n                d[min(x,y)] = set({x,y})\n            elif count == 1:\n                pass\n            else: # union\n                d[min(arr)].update(d[max(arr)])\n                del d[max(arr)]\n            return d\n                \n                    \n        # variables\n        d  = {1:set({1})}\n        da = dict()\n        db = dict()\n        res = 0\n        \n        # sort edges\n        a,b,c= [],[],[]\n        for t,i,j in edges:\n            if t == 3:\n                a.append([t,i,j])\n            elif t == 2:\n                b.append([t,i,j])\n            else:\n                c.append([t,i,j])\n        \n        # main function\n        \n        # t == 3\n        for t,i,j in a:\n            d = find(i,j,d)\n        da = d\n        db = deepcopy(d)\n\n        # t == 2\n        for t,i,j in b:\n            db = find(i,j,db)\n\n        # t == 1\n        for t,i,j in c:\n            da = find(i,j,da)\n            \n        if da[1] == db[1] == set(range(1,n+1)):\n            return res\n        else:\n            return -1", "import copy\n\n\nclass UnionFind:\n    def __init__(self, n):\n        self.parent = list(range(n + 1))\n        \n    def find(self, x):\n        if self.parent[x] != x:\n            self.parent[x] = self.find(self.parent[x])\n        return self.parent[x]\n    \n    def union(self, x, y):\n        px, py = self.find(x), self.find(y)\n        if px == py:\n            return 0\n        \n        self.parent[px] = py\n        return 1\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf, res, e1, e2 = UnionFind(n), 0, 0, 0\n        for _, u, v in [x for x in edges if x[0] == 3]:\n            add = uf.union(u, v)\n            if add:\n                e1 += 1\n                e2 += 1\n            else:\n                res += 1\n                \n        uf1 = copy.deepcopy(uf)\n        for _, u, v in [x for x in edges if x[0] == 1]:\n            add = uf1.union(u, v)\n            if add:\n                e1 += 1\n            else:\n                res += 1        \n                \n        uf2 = uf\n        for _, u, v in [x for x in edges if x[0] == 2]:\n            add = uf2.union(u, v)\n            if add:\n                e2 += 1\n            else:\n                res += 1                    \n        \n        return res if e1 == n - 1 and e2 == n - 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        # add type 3 first \n        if n == 1: return True\n        parentsA = {}\n        \n        def findA(p):\n            if p not in parentsA:\n                parentsA[p] = p\n            if parentsA[p] != p:\n                parentsA[p] = findA(parentsA[p])\n            return parentsA[p]\n    \n        def unionA(p, q):\n            i, j = findA(p), findA(q)\n            if i != j:\n                parentsA[i] = j\n        \n        def isconnectedA(p, q):\n            return findA(p) == findA(q)\n        \n        parentsB = {}\n        \n        def findB(p):\n            if p not in parentsB:\n                parentsB[p] = p\n            if parentsB[p] != p:\n                parentsB[p] = findB(parentsB[p])\n            return parentsB[p]\n    \n        def unionB(p, q):\n            i, j = findB(p), findB(q)\n            if i != j:\n                parentsB[i] = j\n        \n        def isconnectedB(p, q):\n            return findB(p) == findB(q)\n        \n        edges.sort(reverse = True)\n        # first add in best edges\n    \n            \n        skip = 0\n        for typ, fr, to in edges:\n            if typ == 3:\n                if isconnectedA(fr, to) and isconnectedB(fr, to):\n                    skip += 1\n                else:\n                    unionA(fr, to)\n                    unionB(fr, to)\n            elif typ == 1:\n                if isconnectedA(fr, to):\n                    skip += 1\n                else:\n                    unionA(fr, to)\n            elif typ == 2:\n                if isconnectedB(fr, to):\n                    skip += 1\n                else:\n                    unionB(fr, to)\n            # print(typ, fr, to, parentsB)\n                    \n        # print(parentsA)\n        # print(parentsB)\n        \n        allpA = set()\n        for i in range(1, n+1):\n            allpA.add(findA(i))\n        \n        allpB = set()\n        for i in range(1, n+1):\n            allpB.add(findB(i))\n            \n        # print(allpB)\n        if len(allpA) == 1 and len(allpB) == 1: return skip\n        return -1\n                \n                \n                \n                \n                \n                \n                \n                \n                \n                \n                \n            \n            \n", "import collections\nimport sys\nsys.setrecursionlimit(1000000)\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        to = collections.defaultdict(list)\n        for t, a, b in edges:\n            to[a-1].append((b-1,t-1))\n            to[b-1].append((a-1,t-1))\n        \n        def is_connected(etype=0):\n            remain = [1]*n\n            def dfs(n=0):\n                if remain[n] == 0: return\n                remain[n] = 0\n                for nn, t in to[n]:\n                    if t == etype or t == 2:\n                        dfs(nn)\n            dfs()\n            return len([1 for r in remain if r ==1]) == 0\n        if not is_connected(0): return -1\n        if not is_connected(1): return -1\n        \n        ids = [i for i in range(n)]\n        def find(i):\n            if i == ids[i]: return i\n            ni = find(ids[i])\n            ids[i] = ni\n            return ni\n        def union(i, j):\n            i = find(i)\n            j = find(j)\n            if i == j: return False\n            ids[j] = i\n            return True\n        \n        e = 0\n        for t, a, b in edges:\n            if t == 3:\n                if union(a-1, b-1):\n                    e += 1\n        ids2 = list(ids)\n        \n        for t, a, b in edges:\n            if t == 1:\n                if union(a-1, b-1):\n                    e += 1\n                    \n        ids = ids2\n        for t, a, b in edges:\n            if t == 2:\n                if union(a-1, b-1):\n                    e += 1\n        return len(edges) - e\n\n", "class DSU:\n    def __init__(self,n):\n        self.node = list(range(n+1))\n        self.rank = [1]*(n+1)\n    \n    def find(self,x):\n        if self.node[x] != x:\n            self.node[x] = self.find( self.node[x] )\n        return self.node[x]\n    \n    def union(self,x,y):\n        xid, yid = self.find(x), self.find(y)\n        if xid == yid:\n            return False\n        else:\n            xrank = self.rank[xid]\n            yrank = self.rank[yid]\n            if xrank<= yrank:\n                self.node[xid] = yid\n                self.rank[yid] += xrank\n            else:\n                self.node[yid] = xid\n                self.rank[xid] += yrank\n            return True\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        alice = []\n        bob = []\n        both = []\n        for t,u,v in edges:\n            if t==1:\n                alice.append([u,v])\n            elif t==2:\n                bob.append([u,v])\n            elif t==3:\n                both.append([u,v])\n        adsu = DSU(n)\n        bdsu = DSU(n)\n        ans = 0\n        for u,v in both:\n            T1 =  adsu.union(u,v) \n            T2 =  bdsu.union(u,v)\n            if not T1 and not T2:\n                ans += 1\n        for u,v in alice:\n            if not adsu.union(u,v):\n                ans += 1\n        for u,v in bob:\n            if not bdsu.union(u,v):\n                ans += 1\n        for i in range(n+1):\n            adsu.find(i)\n            bdsu.find(i)\n        return ans if len(set(adsu.node))==2 and len(set(bdsu.node))==2 else -1\n                \n        \n        \n        \n        \n\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n\n        def find(x,y,d):\n            nonlocal res\n            count = 0\n            arr = []\n            for key,val in d.items():\n                if x in val and y in val:\n                    res += 1\n                    count = -1\n                    break\n                elif x in val or y in val:\n                    d[key].add(x)\n                    d[key].add(y)\n                    count += 1\n                    arr.append(key)\n            \n            if count == -1:\n                pass\n            elif count == 0:\n                d[min(x,y)] = set({x,y})\n            elif count == 1:\n                pass\n            else: # union\n                d[min(arr)].update(d[max(arr)])\n                del d[max(arr)]\n            return d\n                \n                    \n        # variables\n        d  = {1:set({1})}\n        da = dict()\n        db = dict()\n        res = 0\n        \n        # sort edges\n        a,b,c= [],[],[]\n        for t,i,j in edges:\n            if t == 3:\n                a.append([t,i,j])\n            elif t == 2:\n                b.append([t,i,j])\n            else:\n                c.append([t,i,j])\n        \n        # main function\n        \n        # t == 3\n        for t,i,j in a:\n            d = find(i,j,d)\n        da = deepcopy(d)\n        db = deepcopy(d)\n\n        # t == 2\n        for t,i,j in b:\n            db = find(i,j,db)\n\n        # t == 1\n        for t,i,j in c:\n            da = find(i,j,da)\n            \n        if da[1] == db[1] == set(range(1,n+1)):\n            return res\n        else:\n            return -1", "class DisjointSet:\n    def __init__(self, number_of_sites):\n        self.parent = [i for i in range(number_of_sites)]\n        self.children_site_count = [1 for _ in range(number_of_sites)]\n        self.component_count = number_of_sites\n\n    def find_root(self, site):\n        root = site\n        while root != self.parent[root]:\n            root = self.parent[root]\n        while site != root:\n            site, self.parent[site] = self.parent[site], root\n        return root\n\n    def is_connected(self, site_1, site_2):\n        return self.find_root(site_1) == self.find_root(site_2)\n\n    def union(self, site_1, site_2):\n        site_1_root = self.find_root(site_1)\n        site_2_root = self.find_root(site_2)\n        if site_1_root == site_2_root:\n            return False\n\n        if self.children_site_count[site_1_root] < self.children_site_count[site_2_root]:\n            self.parent[site_1_root] = site_2_root\n            self.children_site_count[site_2_root] += self.children_site_count[\n                site_1_root]\n        else:\n            self.parent[site_2_root] = site_1_root\n            self.children_site_count[site_1_root] += self.children_site_count[\n                site_2_root]\n        self.component_count -= 1\n        return True\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        alice_disjoint_set = DisjointSet(n)\n        bob_disjoint_set = DisjointSet(n)\n\n        TYPE_OF_COMMON_EDGES = 3\n        TYPE_OF_ALICE_EDGES = 1\n        TYPE_OF_BOB_EDGES = 2\n\n        common_edges = filter(lambda edge: edge[0] == TYPE_OF_COMMON_EDGES, edges)\n        alice_edges = filter(lambda edge: edge[0] == TYPE_OF_ALICE_EDGES, edges)\n        bob_edges = filter(lambda edge: edge[0] == TYPE_OF_BOB_EDGES, edges)\n\n        redundant = 0\n        for _, u, v in common_edges:\n            if (not alice_disjoint_set.union(u-1, v-1)) or (not bob_disjoint_set.union(u-1, v-1)):\n                redundant += 1\n\n        for _, u, v in bob_edges:\n            if not bob_disjoint_set.union(u-1,v-1):\n                redundant += 1\n                \n        for _, u, v in alice_edges:\n            if not alice_disjoint_set.union(u-1, v-1):\n                redundant += 1\n        \n        return redundant if alice_disjoint_set.component_count == 1 and bob_disjoint_set.component_count == 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:     \n        graph = {1: [], 2: [], 3: []}\n        for t, i, j in edges:\n            graph[t].append((i, j))\n        def find(x):\n            if parents[x] != x:\n                parents[x] = find(parents[x])\n            return parents[x]\n        \n        def union(x, y):\n            px, py = find(x), find(y)\n            if px == py:\n                return False\n            if rank[px] < rank[py]:\n                px, py = py, px\n            parents[py] = px\n            rank[px] += rank[px] == rank[py]\n            return True\n        \n        result = 0\n        parents = list(range(n + 1))\n        rank = [1] * (n + 1)\n        for i, j in graph[3]:\n            if not union(i, j):\n                result += 1\n        parents_copy = parents[:]\n        rank_copy = rank[:]\n        for i, j in graph[1]:\n            if not union(i, j):\n                result += 1\n        if len(set(find(i) for i in range(1, n + 1))) > 1:\n            return -1\n        parents = parents_copy\n        rank = rank_copy\n        for i, j in graph[2]:\n            if not union(i, j):\n                result += 1\n        if len(set(find(i) for i in range(1, n + 1))) > 1:\n            return -1\n        return result\n", "from collections import defaultdict\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges = [(t, u-1, v-1) for t,u,v in edges]\n        \n        common = []\n        arr = []\n        brr = []\n        \n        for t, u, v in edges:\n            if t == 1:\n                arr.append((u,v))\n            if t == 2:\n                brr.append((u,v))\n            if t == 3:\n                common.append((u,v))\n        \n        d = defaultdict(list)\n        \n        for u,v in common:\n            d[u].append(v)\n            d[v].append(u)\n        \n        sizes = []\n        visited = [False for _ in range(n)]\n        for i in range(n):\n            if visited[i] or not d[i]:\n                continue\n            size = 1\n            stack = [i]\n            visited[i] = True\n            while stack:\n                cur = stack.pop()\n                for nex in d[cur]:\n                    if visited[nex]:\n                        continue\n                    visited[nex] = True\n                    stack.append(nex)\n                    size += 1\n            sizes.append(size)\n        \n        ################################################\n        \n        d = defaultdict(list)\n        for u,v in common:\n            d[u].append(v)\n            d[v].append(u)\n        \n        for u,v in arr:\n            d[u].append(v)\n            d[v].append(u)\n        \n        visited = [False for _ in range(n)]\n            \n        for i in range(n):\n            if visited[i] or not d[i]:\n                continue\n            stack = [i]\n            visited[i] = True\n            while stack:\n                cur = stack.pop()\n                for nex in d[cur]:\n                    if visited[nex]:\n                        continue\n                    visited[nex] = True\n                    stack.append(nex)\n        \n        if not all(visited):\n            return -1\n        \n        ################################################\n        \n        d = defaultdict(list)\n        for u,v in common:\n            d[u].append(v)\n            d[v].append(u)\n        \n        for u,v in brr:\n            d[u].append(v)\n            d[v].append(u)\n        \n        visited = [False for _ in range(n)]\n            \n        for i in range(n):\n            if visited[i] or not d[i]:\n                continue\n            stack = [i]\n            visited[i] = True\n            while stack:\n                cur = stack.pop()\n                for nex in d[cur]:\n                    if visited[nex]:\n                        continue\n                    visited[nex] = True\n                    stack.append(nex)\n        \n        if not all(visited):\n            return -1\n        \n        ################################################\n        \n        \n        expected_commons = sum(x-1 for x in sizes)\n        res = len(common) - expected_commons\n        \n        expected_specific = n - expected_commons - 1\n        res += len(arr) + len(brr) - 2*expected_specific\n\n        print((len(common), expected_commons, len(arr), len(brr), expected_specific))\n        \n        return res\n", "from collections import defaultdict\n\nclass Solution:\n    \n    def find(self, parent, i):\n        if parent[i] == i:\n            return i\n        parent [i] = self.find(parent, parent[i])\n        return parent[i]\n    \n    def union(self, parent, rank, x, y):\n        xroot = self.find(parent, x)\n        yroot = self.find(parent, y)\n  \n        if rank[xroot] < rank[yroot]:\n            parent[xroot] = yroot\n        elif rank[xroot] > rank[yroot]:\n            parent[yroot] = xroot\n        else : \n            parent[yroot] = xroot\n            rank[xroot] += 1\n    \n    def helpMST(self, adj_list, n):\n        \n        parent = []\n        rank = [0] * (n+1)\n        result = []\n        \n        for node in range(n+1): \n            parent.append(node)\n        \n        for t, u, v in adj_list:\n            x = self.find(parent, u)\n            y = self.find(parent, v)\n            \n            if x != y:     \n                result.append((t,u,v)) \n                self.union(parent, rank, x, y)\n                \n        parent_count = 0\n        for index, num in enumerate(parent[1:]):\n            if index + 1 == num:\n                parent_count += 1\n        \n        return parent_count == 1, result\n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        adj_list1 = []\n        adj_list2 = []\n        \n        common = set()\n        for t, u, v in edges:\n            if t == 3:\n                common.add((u, v))\n                adj_list1.append((t,u,v))\n                adj_list2.append((t,u,v))\n        \n        for t, u, v in edges:\n            if t in (1,2) and (u,v) not in common:\n                if t == 1:\n                    adj_list1.append((t,u,v))\n                elif t == 2:\n                    adj_list2.append((t,u,v))\n                    \n        result = set()\n        \n        if len(adj_list1) < n-1 or len(adj_list2) < n-1:\n            return -1\n        elif len(adj_list1) == n-1 and len(adj_list2) == n - 1:\n            result = result.union(set(adj_list2))\n            result = result.union(set(adj_list1))\n            return len(edges) - len(result)\n        \n        if len(adj_list1) > n-1:\n            possible, res = self.helpMST(adj_list1, n)\n            if not possible:\n                return -1\n            result = result.union(set(res))\n        else:\n            result = result.union(set(adj_list1))\n        \n        if len(adj_list2) > n-1:\n            possible, res = self.helpMST(adj_list2, n)\n            if not possible:\n                return -1\n            result = result.union(set(res))\n        else:\n            result = result.union(set(adj_list2))\n            \n        return len(edges) - len(result)\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, e: List[List[int]]) -> int:\n        def union(UF, u, v):            \n            UF[find(UF, v)] = find(UF, u)\n        def find(UF, u):\n            if UF[u] != u: UF[u] = find(UF, UF[u])\n            return UF[u]         \n        def check(UF, t):            \n            UF = UF.copy()\n            for tp, u, v in e:\n                if tp == t: \n                    if find(UF, u) == find(UF, v): self.ans += 1\n                    else: union(UF, u, v)\n            return len(set(find(UF, u) for u in UF)) == 1, UF\n\n        self.ans, UF = 0, {u: u for u in range(1, n+1)}                \n        UF = check(UF, 3)[1]\n        if not check(UF, 1)[0] or not check(UF, 2)[0]: return -1        \n        return self.ans\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        A = [i for i in range(n)]\n        def find(u):\n            if A[u] != u:\n                A[u] = find(A[u])\n            return A[u]\n        def union(u, v):\n            pu, pv = find(u), find(v)\n            if pu == pv:\n                return False\n            A[max(pu,pv)] = min(pu,pv)\n            return True\n        \n        #\u5148\u505aCommon\u7684\u904d\u5386\n        edges = sorted(edges, reverse=True)\n        # print(\\\"\u770b\u770b\u987a\u5e8f\u5bf9\u4e0d\u5bf9\\\", edges)\n        \n        i = 0\n        m = len(edges)\n        res = 0\n        while i < m:\n            cur = edges[i]\n            if cur[0] == 3:\n                #\u4ece0\u5f00\u59cb\u7f16\u53f7\n                if union(cur[1]-1,cur[2]-1) == False:\n                    res += 1\n            else:\n                break\n            i += 1\n        # print(\\\"common res\\\", res)\n        \n        #\u627eBob\u7684\n        origin_A = deepcopy(A)\n        while i < m:\n            cur = edges[i]\n            if cur[0] == 2:\n                #\u4ece0\u5f00\u59cb\u7f16\u53f7\n                if union(cur[1]-1,cur[2]-1) == False:\n                    res += 1\n            else:\n                break\n            i += 1\n        # print(\\\"Bob and Common\\\", res, A)\n        for j in range(1, n):\n            #\u67e5\u770b\u662f\u5426\u6240\u6709\u7684\u8282\u70b9\u90fd\u80fd\u591f\u52300\n            # print(j)\n            p = find(j)\n            if  p != 0:\n                # print(\\\"Bob \\\" + str(j) + \\\" can't to 0, only to \\\" + str(p))\n                return -1\n        \n        #Alice\n        A = origin_A\n        while i < m:\n            cur = edges[i]\n            if cur[0] == 1:\n                #\u4ece0\u5f00\u59cb\u7f16\u53f7\n                if union(cur[1]-1,cur[2]-1) == False:\n                    res += 1\n            else:\n                break\n            i += 1\n            \n        # print(\\\"Alice and Common\\\", A)\n        for j in range(1, n):\n            #\u67e5\u770b\u662f\u5426\u6240\u6709\u7684\u8282\u70b9\u90fd\u80fd\u591f\u52300\n            p = find(j)\n            if p != 0:\n                # print(\\\"Alice \\\" + str(j) + \\\" can't to 0, only to \\\" + str(p))\n                return -1\n            \n        return res\n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        ufa = UnionFind(n) # Graph for Alice\n        ufb = UnionFind(n) # Graph for Bob\n        cnt = 0 # number of removable edges\n        \n        for x, y, z in edges:\n            if x == 3:\n                flag1 = ufa.union(y, z)\n                flag2 = ufb.union(y, z)\n                if not flag1 or not flag2: cnt +=1\n\n        for x, y, z in edges:\n            if x == 1:\n                flag = ufa.union(y, z)\n                if not flag: cnt += 1\n            if x == 2:\n                flag = ufb.union(y, z)\n                if not flag: cnt += 1\n\n        return cnt if ufa.groups == 1 and ufb.groups == 1 else -1\n            \n        \nclass UnionFind():\n    def __init__(self, n):\n        self.parents = {i:i for i in range(1, n+1)}\n        self.groups = n\n\n    def find(self, x):\n        if self.parents[x] == x:\n            return x\n        else:\n            self.parents[x] = self.find(self.parents[x])\n            return self.parents[x]\n\n    def union(self, x, y):\n        x = self.find(x)\n        y = self.find(y)\n\n        if x == y:\n            return False\n\n        self.parents[y] = x\n        self.groups -= 1\n        return True", "class DSU:\n    def __init__(self, size):\n        self.indexes = {i:i for i in range(size)}\n        self.sizes = {i:1 for i in range(size)}\n        self.com = size\n        \n    def root(self, i):\n        node = i\n        while i!=self.indexes[i]:\n            i = self.indexes[i]\n        \n        while node!=i:\n            nnode = self.indexes[node]\n            self.indexes[node] = i\n            node = nnode\n            \n        return i\n    \n    def unite(self, i, j):\n        ri , rj = self.root(i), self.root(j)\n        if ri==rj:\n            return\n        \n        self.indexes[ri] = rj\n        self.sizes[rj] += self.sizes[ri]\n        self.com-=1\n        \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        alice = DSU(n)\n        bob = DSU(n)\n        \n        ans = 0\n        \n        for t, u, v in edges:\n            if t==3:\n                aru, arv = alice.root(u-1), alice.root(v-1)\n                bru, brv = bob.root(u-1), bob.root(v-1)\n                if aru==arv and bru==brv:\n                    ans += 1\n                else:\n                    alice.unite(u-1, v-1)\n                    bob.unite(u-1, v-1)\n        \n                \n        \n        for t,u,v in edges:\n            if t==1:\n                ru, rv = alice.root(u-1), alice.root(v-1)\n                if ru==rv:\n                    ans += 1\n                else:\n                    alice.unite(u-1, v-1)\n                    \n            elif t==2:\n                ru, rv = bob.root(u-1), bob.root(v-1)\n                if ru==rv:\n                    ans += 1\n                else:\n                    bob.unite(u-1, v-1)\n                \n        \n        if alice.com!=1 or bob.com!=1:\n            return -1\n        \n        return ans\n    \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges.sort(key=lambda x: (-x[0], x[1]))\n        removed = set()\n        \n        def find_root(parents, i):\n            if parents[i] == i:\n                return i, 0\n            a, b = find_root(parents, parents[i])\n            return a, b+1\n        \n        def uf(n, edges, types):\n            parents = [x for x in range(n+1)]\n            group = 1\n            for type, a, b in edges:\n                \n                if type not in types:\n                    continue\n                r_a, n_a = find_root(parents, a)\n                r_b, n_b = find_root(parents, b)\n                if r_a != r_b:\n                    if n_a >= n_b:\n                        parents[r_b] = r_a\n                    else:\n                        parents[r_a] = r_b\n                    group+=1\n                else:\n                    removed.add((type, a, b))\n            return group == n\n            \n        \n        if not uf(n, edges, [3,1]):\n            return -1\n        if not uf(n, edges, [3,2]):\n            return -1\n        return len(removed)", "# awice\nclass DSU:\n    def __init__(self, N):\n        self.parents = list(range(N))\n        self.sz = [1]*N\n    \n    def find(self, u):\n        if self.parents[u] != u:\n            self.parents[u] = self.find(self.parents[u])\n        return self.parents[u]\n    \n    def union(self, u, v):\n        pu, pv = self.find(u), self.find(v)\n        if pu == pv:    return False\n        if self.sz[pv] > self.sz[pu]:\n            pu, pv = pv, pu\n        self.parents[pv] = pu\n        self.sz[pu] += self.sz[pv]\n        self.sz[pv] = self.sz[pu]\n        \n        return True\n    \n    def get_sz(self, u):\n        return self.sz[self.find(u)]\n        \nclass Solution:\n    # weight edges with full link weighted 2?\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # union find\n        alice, bob, full = [], [], []\n        \n        for e in edges:\n            u, v = e[1]-1, e[2]-1\n            if e[0] == 1:   alice.append([u,v])\n            elif e[0] == 2: bob.append([u,v])\n            else:   full.append([u,v])\n        \n        res = 0\n        dsua, dsub = DSU(n), DSU(n)\n        for u, v in full:\n            dsua.union(u, v)\n            if not dsub.union(u, v):\n                res += 1\n            \n        for u, v in alice:\n            if not dsua.union(u, v):\n                res += 1\n        for u, v in bob:\n            if not dsub.union(u, v):\n                res += 1\n        \n        if dsua.get_sz(0) != n or dsub.get_sz(0) != n:  return -1\n        return res\n", "class DSU:\n    def __init__(self, n):\n        self.roots = [i for i in range(n)]\n    def find(self, x):\n        if self.roots[x] != x:\n            self.roots[x] = self.find(self.roots[x])\n        return self.roots[x]\n    def union(self, x, y):\n        self.roots[self.find(x)] = self.find(y)\n        \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        m = len(edges)\n        edges = sorted(edges, key=lambda x: -x[0])\n        type3_exist = set()\n        removed = set()\n        for i in range(len(edges)):\n            typ, n1, n2 = edges[i]\n            if (n1, n2) in type3_exist:\n                removed.add(i)\n                continue\n                \n            if typ == 3:\n                type3_exist.add((n1, n2))\n        \n        edges = [edge for idx, edge in enumerate(edges) if idx not in removed]\n        \n        dsu_alice = DSU(n)\n        dsu_bob = DSU(n)\n        \n        count = 0\n        for edge in edges:\n            typ, n1, n2 = edge\n            if typ == 1:\n                if dsu_alice.find(n1-1) != dsu_alice.find(n2-1):\n                    count += 1\n                    dsu_alice.union(n1-1, n2-1)\n            if typ == 2:\n                if dsu_bob.find(n1-1) != dsu_bob.find(n2-1):\n                    count += 1\n                    dsu_bob.union(n1-1, n2-1)\n            if typ == 3:\n                if dsu_bob.find(n1-1) != dsu_bob.find(n2-1) or dsu_alice.find(n1-1) != dsu_alice.find(n2-1):\n                    count += 1\n                    dsu_alice.union(n1-1, n2-1)\n                    dsu_bob.union(n1-1, n2-1)\n        \n        if len(set([dsu_bob.find(i) for i in range(n)])) != 1 or len(set([dsu_alice.find(i) for i in range(n)])) != 1:\n            return -1\n        \n        return m - count", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        alice = DSU()\n        bob = DSU()\n        count = 0\n        for t, u, v in edges:\n            if t == 3:\n                if alice.find(u) == alice.find(v) or bob.find(u) == bob.find(v):\n                    count += 1\n                else:\n                    alice.union(u, v)\n                    bob.union(u, v)\n        \n        for t, u, v in edges:\n            if t == 1:\n                if alice.find(u) == alice.find(v):\n                    count += 1\n                else:\n                    alice.union(u, v)\n        \n        for t, u, v in edges:\n            if t == 2:\n                if bob.find(u) == bob.find(v):\n                    count += 1       \n                else:\n                    bob.union(u, v)\n                    \n        if max(bob.count.values()) != n or max(alice.count.values()) != n:\n            return -1\n        \n        return count\n        \nclass DSU:\n    def __init__(self):\n        self.father = {}\n        self.count = {}\n    \n    def find(self, a):\n        self.father.setdefault(a, a)\n        self.count.setdefault(a, 1)\n        if a != self.father[a]:\n            self.father[a] = self.find(self.father[a])\n        return self.father[a]\n    \n    def union(self, a, b):\n        _a = self.find(a)\n        _b = self.find(b)\n        if _a != _b:\n            self.father[_a] = self.father[_b]\n            self.count[_b] += self.count[_a]\n            \n            \n            \n            \n", "class DSU:\n    def __init__(self, n):\n        self.p = list(range(n + 1))\n        self.isolated_nodes = n\n        \n    def find(self, x):\n        if x != self.p[x]:\n            self.p[x] = self.find(self.p[x])\n        return self.p[x]\n    \n    def union(self, x, y):\n        xr = self.find(x)\n        yr = self.find(y)\n        \n        if xr != yr:\n            self.isolated_nodes -= 1\n            \n        self.p[xr] = yr\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        #keep two union for Bob and Alice\n        #add edges to the dsu, if the two nodes are already in a union, we can delete the edge\n        #always add type3 first\n        edges.sort(reverse = True)\n        \n        dsuA = DSU(n)\n        dsuB = DSU(n)\n        res = 0\n        for [t, n1, n2] in edges:\n            if t == 3:\n                #handle Alice\n                if dsuA.find(n1) == dsuA.find(n2) and dsuB.find(n1) == dsuB.find(n2):\n                    #don't add the edge\n                    res += 1\n                else:\n                    dsuA.union(n1, n2)\n                    dsuB.union(n1, n2)\n            elif t == 1:\n                #Alice\n                if dsuA.find(n1) == dsuA.find(n2):\n                    res += 1\n                else:\n                    dsuA.union(n1, n2)\n            else:\n                if dsuB.find(n1) == dsuB.find(n2):\n                    res += 1\n                else:\n                    dsuB.union(n1, n2)\n                    \n        return res if dsuA.isolated_nodes == 1 and dsuB.isolated_nodes == 1 else -1\n                \n", "from collections import defaultdict\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        d = defaultdict(list)\n        for t, u, v in edges:\n            d[t].append((u - 1, v - 1))\n            \n        bob, alice = list(range(n)), list(range(n))\n        \n        def find(x, is_bob):\n            if is_bob:\n                if x != bob[x]:\n                    bob[x] = find(bob[x], is_bob)\n                return bob[x]\n            else:\n                if x != alice[x]:\n                    alice[x] = find(alice[x], is_bob)\n                return alice[x]\n            \n        res = 0\n        for t in [3, 2, 1]:\n            for u, v in d[t]:\n                if t == 3:\n                    rootu, rootv = find(u, True), find(v, True)\n                    if rootu != rootv:\n                        bob[rootu] = rootv\n                        alice[rootu] = rootv\n                        res += 1\n                elif t == 1:\n                    rootu, rootv = find(u, False), find(v, False)\n                    if rootu != rootv:\n                        alice[rootu] = rootv\n                        res += 1\n                else:\n                    rootu, rootv = find(u, True), find(v, True)\n                    if rootu != rootv:\n                        bob[rootu] = rootv\n                        res += 1\n                        \n        root_bob, root_alice = find(0, True), find(0, False)\n        if all(find(num, True) == root_bob for num in bob) and all(find(num, False) == root_alice for num in alice):\n            return len(edges) - res\n        else:\n            return -1", "class Solution:\n    def maxNumEdgesToRemove(self, n, edges):\n        # Union find\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n\n        res = e1 = e2 = 0\n\n        # Alice and Bob\n        root = list(range(n + 1))\n        for t, i, j in edges:\n            if t == 3:\n                if uni(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        root0 = root[:]\n\n        # only Alice\n        for t, i, j in edges:\n            if t == 1:\n                if uni(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n\n        # only Bob\n        root = root0\n        for t, i, j in edges:\n            if t == 2:\n                if uni(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if e1 == e2 == n - 1 else -1", "class unionFindSet:\n    def __init__(self, S):\n        self.parent = {i:i for i in S}\n        self.rank = {i:1 for i in S}\n        self.count = len(S) #number of groups\n        \n    def find(self, u):\n        if u not in self.parent:\n            return -1\n        path = []\n        while self.parent[u]!=u:\n            path.append(u)\n            u = self.parent[u]\n        for p in path: #make tree flat\n          \\tself.parent[p] = u\n        return u\n    \n    def union(self, u, v):\n        pu, pv = self.find(u), self.find(v)\n        if pu==pv:\n            return False\n        if self.rank[pu] > self.rank[pv]: #union by rank\n            pu, pv = pv, pu\n        self.parent[pu] = pv #pu not u !!!\n        self.rank[pv] += 1\n        self.count -= 1\n        return True\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        #edges = set([tuple(i) for i in edges])\n        data = unionFindSet([i for i in range(1, n + 1)])\n        e1, e2, res = 0, 0, 0\n        edges3 = [(t, u, v) for t, u, v in edges if t==3] \n        edges1 = [(t, u, v) for t, u, v in edges if t==1] \n        edges2 = [(t, u, v) for t, u, v in edges if t==2]\n        ## 2 + 3\n        for i in range(len(edges3)):\n            t, u, v = edges3[i]\n            if data.find(u) != data.find(v):\n                data.union(u, v)\n                e1 += 1\n                e2 += 1\n            else:\n                res += 1\n        data2 = copy.deepcopy(data) ##########\n        \n        for i in range(len(edges2)):\n            t, u, v = edges2[i]\n            if data.find(u) != data.find(v):\n                data.union(u, v)\n                e2 += 1\n            else:\n                res += 1\n        if data.count != 1:\n            return -1\n        \n        ## 2 + 3\n        for i in range(len(edges1)):\n            t, u, v = edges1[i]\n            if data2.find(u) != data2.find(v):\n                data2.union(u, v)\n                e1 += 1\n            else:\n                res += 1\n        if data2.count != 1:\n            return -1\n\n        return res\n    #\u5148 uf \u516c\u5171\u8fb9\u7136\u540e\u5b58\u8d77\u6765 \u7136\u540e\u5206\u522b\u5bf9 alice \u548c bob \u505a\u4e00\u4e0b\n\n\n                \n            \n            \n        \n        \n        \n        ", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges1 = defaultdict(list)\n        edges2 = defaultdict(list)\n        \n        for t,a,b in edges:\n            if t == 3:\n                edges1[a-1].append([0,b-1])\n                edges1[b-1].append([0,a-1])\n                edges2[a-1].append([0,b-1])\n                edges2[b-1].append([0,a-1])\n            elif t == 1:\n                edges1[a-1].append([1,b-1])\n                edges1[b-1].append([1,a-1])\n            else:\n                edges2[a-1].append([1,b-1])\n                edges2[b-1].append([1,a-1])\n        dis1 = [0] + [1] * (n-1)\n        dis2 = [0] + [1] * (n-1)\n        m = 0\n        que1 = []\n        que2 = []\n        both = []\n        for a,b in edges1[0]:\n            if a == 0 :\n                both.append(b)\n            else:\n                que1.append(b)\n        for a,b in edges1[0]:\n            if a == 0 :\n                continue\n            else:\n                que2.append(b)           \n        \n        while both or que1:\n            if both:\n                D = both.pop()\n                if dis1[D]:\n                    m+= 1\n                    dis1[D] = dis2[D] = 0\n                else:\n                    continue\n            else:\n                D = que1.pop()\n                if dis1[D]:\n                    m+= 1\n                    dis1[D] = 0\n                else:\n                    continue\n            for c,dd in edges1[D]:\n                if c:\n                    que1.append(dd)\n                else:\n                    both.append(dd)\n            edges1[D] = []\n            \n            if dis2[D] == 0:\n                for c,dd in edges2[D]:\n                    if c:\n                        que2.append(dd)\n                 \n        while both or que2:\n            if both:\n                D = both.pop()\n                if dis2[D]:\n                    m+= 1\n                    dis1[D] = dis2[D] = 0\n                else:\n                    pass\n            else:\n                D = que2.pop()\n                if dis2[D]:\n                    m+= 1\n                    dis2[D] = 0\n                else:\n                    pass\n            for c,dd in edges2[D]:\n                if c:\n                    que2.append(dd)\n                else:\n                    both.append(dd)\n            edges2[D] = []    \n        print((dis1,dis2))\n        if 1 in dis1 or 1 in dis2:\n            return -1\n        return len(edges) - m\n", "from collections import defaultdict\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n, edges) -> int:\n        e = [defaultdict(lambda: set()) for i in range(3)]\n        d = 0\n        for edge in edges:\n            t, x, y = edge\n            x -= 1\n            y -= 1\n            t -= 1\n            if y in e[t][x]:\n                d += 1\n            else:\n                e[t][x].add(y)\n                e[t][y].add(x)\n\n        q = 0\n        for i in range(n):\n            for j in e[2][i]:\n                for k in range(2):\n                    if j in e[k][i]:\n                        q += 1\n                        e[k][i].remove(j)\n        q = q >> 1\n\n        def visit(v, c, t, o):\n            v[c] = o\n            for i in e[t][c]:\n                if v[i] == -1:\n                    visit(v, i, t, o)\n            if t != 2:\n                for i in e[2][c]:\n                    if v[i] == -1:\n                        visit(v, i, t, o)\n\n        def solve(t):\n            v = [-1] * n\n            p = 0\n            for i in range(n):\n                if v[i] == -1:\n                    visit(v, i, t, p)\n                    p += 1\n\n            mp = [0] * p\n            cp = [0] * p\n            for i in range(n):\n                mp[v[i]] += len(e[t][i])\n                cp[v[i]] += 1\n\n            return p, mp, cp, v\n\n        r = d + q\n        p2, mp2, cp2, vp2 = solve(2)\n        for i in range(p2):\n            r += (mp2[i] >> 1) - (cp2[i] - 1)\n\n        rr = [solve(t) for t in range(2)]\n        if rr[0][0] != 1 or rr[1][0] != 1:\n            return -1\n\n        tp = p2 + (n - sum(cp2))\n        for k in range(2):\n            ee = 0\n            tt = 0\n            for i in range(n):\n                for j in e[k][i]:\n                    if vp2[i] != vp2[j]:\n                        ee += 1\n                    else:\n                        tt += 1\n            ee = ee >> 1\n            tt = tt >> 1\n            r += ee - (tp - 1) + tt\n\n        return r\n", "class DisjointSet:\n    def __init__(self, number_of_sites):\n        self.parent = [i for i in range(number_of_sites+1)]\n        self.children_site_count = [1 for _ in range(number_of_sites+1)]\n        self.component_count = number_of_sites\n\n    def find_root(self, site):\n        root = site\n        if self.parent[site] != site:\n            self.parent[site] = self.find_root(self.parent[site])\n        return self.parent[site]\n\n    def is_connected(self, site_1, site_2):\n        return self.find_root(site_1) == self.find_root(site_2)\n\n    def union(self, site_1, site_2):\n        site_1_root = self.find_root(site_1)\n        site_2_root = self.find_root(site_2)\n        if site_1_root == site_2_root:\n            return False\n\n        if self.children_site_count[site_1_root] < self.children_site_count[site_2_root]:\n            self.parent[site_1_root] = site_2_root\n            self.children_site_count[site_2_root] += self.children_site_count[\n                site_1_root]\n        else:\n            self.parent[site_2_root] = site_1_root\n            self.children_site_count[site_1_root] += self.children_site_count[\n                site_2_root]\n        self.component_count -= 1\n        return True\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        alice_disjoint_set = DisjointSet(n)\n        bob_disjoint_set = DisjointSet(n)\n\n        TYPE_OF_COMMON_EDGES = 3\n        TYPE_OF_ALICE_EDGES = 1\n        TYPE_OF_BOB_EDGES = 2\n\n        common_edges = filter(lambda edge: edge[0] == TYPE_OF_COMMON_EDGES, edges)\n        alice_edges = filter(lambda edge: edge[0] == TYPE_OF_ALICE_EDGES, edges)\n        bob_edges = filter(lambda edge: edge[0] == TYPE_OF_BOB_EDGES, edges)\n\n        redundant = 0\n        for _, u, v in common_edges:\n            unioned_in_alice = alice_disjoint_set.union(u, v)\n            unioned_in_bob = bob_disjoint_set.union(u, v)\n            if unioned_in_alice and unioned_in_bob:\n                continue\n            else:\n                redundant += 1\n\n        for _, u, v in bob_edges:\n            if not bob_disjoint_set.union(u,v):\n                redundant += 1\n                \n        for _, u, v in alice_edges:\n            if not alice_disjoint_set.union(u, v):\n                redundant += 1\n        \n        return redundant if alice_disjoint_set.component_count == 1 and bob_disjoint_set.component_count == 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, N: int, E: List[List[int]], same = 0) -> int:\n        E = [[_, u - 1, v - 1] for _, u, v in E]                    # \u2b50\ufe0f -1 for 1-based to 0-based indexing\n        A = [i for i in range(N)]                                   # \\\\U0001f642 parent representatives of disjoint sets for Alice\n        B = [i for i in range(N)]                                   # \\\\U0001f642 parent representatives of disjoint sets for Bob\n        def find(P, x): P[x] = P[x] if P[x] == x else find(P, P[x]); return P[x]\n        def union(P, a, b):\n            a = find(P, a)\n            b = find(P, b)\n            if a == b:\n                return 1\n            P[a] = b  # arbitrary choice\n            return 0\n        for type, u, v in E:\n            if type == 3: same += union(A, u, v) | union(B, u, v)   # \\\\U0001f947 first: \\\\U0001f517 union \u2705 shared edges between Alice and Bob\n        for type, u, v in E:\n            if type == 1: same += union(A, u, v)                    # \\\\U0001f948 second: \\\\U0001f517 union \\\\U0001f6ab non-shared edges between Alice and Bob\n            if type == 2: same += union(B, u, v)\n        # \\\\U0001f3af is there a single connected component for Alice and Bob?\n        # if so, return the accumulated amount of edges which redundantly connect\n        # to each same connected component correspondingly for Alice and Bob\n        return same if all(find(A, 0) == find(A, x) for x in A) and all(find(B, 0) == find(B, x) for x in B) else -1\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        m = len(edges)\n        \n        g1 = [set() for i in range(n)]\n        g2 = [set() for i in range(n)]\n        g = [[] for i in range(n)]\n        for t, u, v in edges:\n            u -= 1\n            v -= 1\n            if t != 1:\n                g2[u].add(v)\n                g2[v].add(u)\n                \n            if t != 2:\n                g1[u].add(v)\n                g1[v].add(u)\n                \n            if t == 3:\n                g[u].append(v)\n                g[v].append(u)\n                \n        vs = [False] * n\n        stk = [0]\n        \n        while stk:\n            idx = stk.pop()\n            vs[idx] = True\n            \n            for ne in g1[idx]:\n                if not vs[ne]:\n                    stk.append(ne)\n        \n        for i in range(n):\n            if not vs[i]:\n                return -1\n        \n        \n        vs = [False] * n\n        stk = [0]\n        \n        while stk:\n            idx = stk.pop()\n            vs[idx] = True\n            \n            for ne in g2[idx]:\n                if not vs[ne]:\n                    stk.append(ne)\n        \n        \n        for i in range(n):\n            if not vs[i]:\n                return -1\n        \n        vs = [False] * n\n        color = 0\n        count = 0\n        for i in range(n):\n            if not vs[i]:\n                color += 1\n                tc = 0\n                stk = [i]\n                while stk:\n                    idx = stk.pop()\n                    if not vs[idx]:\n                        vs[idx] = True\n                        tc += 1\n                    \n                    for ne in g[idx]:\n                        if not vs[ne]:\n                            stk.append(ne)\n                            \n                count += tc - 1\n                \n        print(count)\n        print(color)\n        \n        return m - (count + (color * 2 - 2))\n                    \n", "from collections import defaultdict\n\nclass Solution:\n    def dfs1(self, n, edges):\n        neighbours = dict()\n        visited = set()\n        for i in range(1,n+1):\n            neighbours[i] = set()\n        for [t, u, v] in edges:\n            if t in [1,3]:\n                neighbours[u].add(v)\n                neighbours[v].add(u)\n                \n        def dfs(u):\n            visited.add(u)\n            for v in neighbours[u]:\n                if v not in visited:\n                    dfs(v)\n\n        dfs(1)\n        return len(visited) == n\n                \n    def dfs2(self, n, edges):\n        neighbours = dict()\n        visited = set()\n        for i in range(1,n+1):\n            neighbours[i] = set()\n        for [t, u, v] in edges:\n            if t in [2,3]:\n                neighbours[u].add(v)\n                neighbours[v].add(u)\n                \n        def dfs(u):\n            visited.add(u)\n            for v in neighbours[u]:\n                if v not in visited:\n                    dfs(v)\n                    \n        dfs(1)\n        return len(visited) == n\n\n       \n    def dfs3(self, n, edges):\n        neighbours = dict()\n        visited = dict()\n        \n        for i in range(1,n+1):\n            neighbours[i] = set()\n        for [t, u, v] in edges:\n            if t in [3]:\n                neighbours[u].add(v)\n                neighbours[v].add(u)\n                \n        def dfs(daddy, u):\n            visited[u] = daddy\n            for v in neighbours[u]:\n                if v not in visited:\n                    dfs(daddy, v)\n                    \n        for i in range(1, n+1):\n            if i not in visited:\n                dfs(i,i)\n                \n        cc_labels = set()\n        cc_size = defaultdict(int)\n        \n        for u in visited:\n            cc_labels.add(visited[u])\n            cc_size[visited[u]] += 1\n        \n        num_cc = len(cc_labels)\n        \n        ret = 2*(num_cc - 1)\n        # print(\\\"num_cc=\\\", num_cc)\n        # print(\\\"adding\\\", ret)\n        for u in cc_size:\n            # print(\\\"adding\\\", cc_size[u] - 1)\n            ret += (cc_size[u] - 1)\n        \n        return ret\n                \n        \n        \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        if not (self.dfs1(n, edges) and self.dfs2(n, edges)):\n            return -1\n        \n        return len(edges) - self.dfs3(n, edges)\n", "class UnionFindSet:\n    def __init__(self, n):\n        self.parents = list(range(n))\n        self.ranks = [1] * n\n        self.size = 1\n        \n    def find(self, u):\n        if u != self.parents[u]:\n            self.parents[u] = self.find(self.parents[u])\n        return self.parents[u]\n    \n    def union(self, u, v):\n        pu, pv = self.find(u), self.find(v)\n        if pu == pv:\n            return False\n        if self.ranks[pu] > self.ranks[pv]:\n            self.parents[pv] = pu\n        elif self.ranks[pv] > self.ranks[pu]:\n            self.parents[pu] = pv\n        else:\n            self.parents[pu] = pv\n            self.ranks[pv] += 1\n        self.size += 1\n        return True\n    \n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1, uf2 = UnionFindSet(n), UnionFindSet(n)\n        ans = 0\n        for t, u, v in edges:\n            if t != 3:\n                continue\n            if not uf1.union(u - 1, v - 1) or not uf2.union(u - 1, v - 1):\n                ans += 1\n        \n        for t, u, v in edges:\n            if t == 1 and not uf1.union(u - 1, v - 1):\n                ans += 1\n            elif t == 2 and not uf2.union(u - 1, v - 1):\n                ans += 1\n                \n        if uf1.size != n or uf2.size != n: return -1\n        return ans", "\\\"\\\"\\\"\nN connected nodes, M edges (M <= N*(N-1)//2)\nwhat is the minimum number of edges to connect N nodes?\n\n\n\n\\\"\\\"\\\"\n\n\n\nclass UnionFind:\n    def __init__(self, n):\n        self.parent = list(range(n))\n        self.ranks = [1] * n\n        self.size = 1\n        \n    def find(self, u):\n        if u == self.parent[u]:\n            return self.parent[u]\n        return self.find(self.parent[u])\n    \n    def union(self, i, j):\n        x = self.find(i)\n        y = self.find(j)\n        if x == y:\n            return False\n        if self.ranks[x] > self.ranks[y]:\n            self.parent[y] = x\n        elif self.ranks[y] > self.ranks[x]:\n            self.parent[x] = y\n        else:\n            self.parent[x] = y\n            self.ranks[y] += 1\n        self.size += 1\n        return True\n    \n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1 = UnionFind(n)\n        uf2 = UnionFind(n)\n        res = 0\n        \n        for t, u, v in edges:\n            if t != 3:\n                continue\n            if not uf1.union(u - 1, v - 1) or not uf2.union(u - 1, v - 1):\n                res += 1\n        \n        for t, u, v in edges:\n            if t == 1 and not uf1.union(u - 1, v - 1):\n                res += 1\n            elif t == 2 and not uf2.union(u - 1, v - 1):\n                res += 1\n   \n        return res if uf1.size == n and uf2.size == n else -1\n\n\n\n\n        \n        \n        \n        \n        \n        ", "class UnionFind(object):\n    def __init__(self, n, recursion = False):\n        self._par = list(range(n))\n        self._size = [1] * n\n        self._recursion = recursion\n\n    def root(self, k):\n        if self._recursion:\n            if k == self._par[k]:\n                return k\n            self._par[k] = self.root(self._par[k])\n            return self._par[k]\n        else:\n            root = k\n            while root != self._par[root]: root = self._par[root]\n            while k != root: k, self._par[k] = self._par[k], root\n            return root\n\n    def unite(self, i, j):\n        i, j = self.root(i), self.root(j)\n        if i == j: return False\n        if self._size[i] < self._size[j]: i, j = j, i\n        self._par[j] = i\n        self._size[i] += self._size[j]\n        return True\n\n    def is_connected(self, i, j):\n        return self.root(i) == self.root(j)\n\n    def size(self, k):\n        return self._size[self.root(k)]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1 = UnionFind(n)\n        uf2 = UnionFind(n)\n        ans = 0\n        \n        for i, u, v in edges:\n            if i == 3:\n                ans += 1 - uf1.unite(u - 1, v - 1)\n                uf2.unite(u - 1, v - 1)\n        \n        for i, u, v in edges:\n            if i == 1:\n                ans += 1 - uf1.unite(u - 1, v - 1)\n            elif i == 2:\n                ans += 1 - uf2.unite(u - 1, v - 1)\n        \n        if uf1.size(0) == uf2.size(0) == n:\n            return ans\n        else:\n            return -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # Union find\n#         def find(i):\n#             if i != root[i]:\n#                 root[i] = find(root[i])\n#             return root[i]\n\n#         def uni(x, y):\n#             x, y = find(x), find(y)\n#             if x == y: return 0\n#             root[x] = y\n#             return 1\n\n#         res = e1 = e2 = 0\n\n#         # Alice and Bob\n#         root = list(range(n + 1))\n#         for t, i, j in edges:\n#             if t == 3:\n#                 if uni(i, j):\n#                     e1 += 1\n#                     e2 += 1\n#                 else:\n#                     res += 1\n#         root0 = root[:]\n\n#         # only Alice\n#         for t, i, j in edges:\n#             if t == 1:\n#                 if uni(i, j):\n#                     e1 += 1\n#                 else:\n#                     res += 1\n\n#         # only Bob\n#         root = root0\n#         for t, i, j in edges:\n#             if t == 2:\n#                 if uni(i, j):\n#                     e2 += 1\n#                 else:\n#                     res += 1\n\n#         return res if e1 == e2 == n - 1 else -1\n        \n        def findRoot(a):\n            if a != root[a]:\n                root[a] = findRoot(root[a])\n            return root[a]\n        \n        def union(a,b):\n            aRoot,bRoot = findRoot(a),findRoot(b)\n            if aRoot != bRoot:\n                root[aRoot] = bRoot\n                return True\n            return False\n        \n        root = [i for i in range(n+1)]\n        numofEdgesAli,numofEdgesBob,ans = 0,0,0\n        for t,a,b in edges:\n            if t == 3:\n                if union(a,b): \n                    numofEdgesAli += 1\n                    numofEdgesBob += 1\n                else: \n                    ans += 1\n        root0 = root[:]\n        for t,a,b in edges:\n            if t == 1:\n                if union(a,b): \n                    numofEdgesAli += 1\n                else: \n                    ans += 1\n        root = root0\n        for t,a,b in edges:\n            if t == 2:\n                if union(a,b):\n                    numofEdgesBob += 1\n                else: \n                    ans += 1\n        return ans if numofEdgesAli == numofEdgesBob == n-1 else -1\n                \n                \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        ufAli = uf(n)\n        ufBob = uf(n)\n        \n#         10 -> 2\n        for edg in edges:\n            x, y = edg[1], edg[2]\n            if edg[0] == 1:\n                ufAli.addEdge(x, y, 2)\n            elif edg[0] == 2:\n                ufBob.addEdge(x, y, 2)\n            else:\n                ufAli.addEdge(x, y, 1)\n                ufBob.addEdge(x, y, 1)\n                \n        # print(ufAli.g, ufAli.kruskalmst())\n        # print(ufBob.g, ufBob.kruskalmst())\n        \n        blueremoved = set()\n        aliremoved = set()\n        bobremoved = set()\n        \n        ans1 = ufAli.kruskalmst(blueremoved, aliremoved)\n        ans2 = ufBob.kruskalmst(blueremoved, bobremoved)\n        if ans1 == -1 or ans2 == -1:\n            return -1\n        \n        # return ans1 + ans2\n        return len(blueremoved) + len(aliremoved) + len(bobremoved)\n        \n        \n                \n        \n        \n        \n\nclass uf:\n    def __init__(self, n):\n        self.n = n\n        self.g = []\n        self.joinednodes = set()\n        # self.totalnodes = set()\n        \n        \n    def addEdge(self, x, y, cost):\n        self.g.append((x, y, cost))\n        # self.joinednodes \n        \n    def find(self, x, parent):\n        if parent[x] == x:\n            return x\n        \n        return self.find(parent[x], parent)\n    \n    def union(self, x, y, parent, rank):\n        xroot, yroot = self.find(x, parent), self.find(y, parent)\n        \n        if xroot != yroot:\n            if rank[xroot] > rank[yroot]:\n                parent[yroot] = xroot\n            elif rank[yroot] > rank[xroot]:\n                parent[xroot] = yroot\n            else:\n                parent[yroot] = xroot\n                rank[xroot] += 1\n                \n    def kruskalmst(self, blue, rorg):\n        # parent = { for edge in g}\n        parent = {}\n        rank = {}\n        for edge in self.g:\n            parent[edge[0]] = edge[0]\n            parent[edge[1]] = edge[1]\n            rank[edge[0]] = 0\n            rank[edge[1]] = 0\n            \n        # print(parent, rank)\n        success = 0\n        self.g.sort(key=lambda edge: edge[2])\n        for edge in self.g:\n            x, y, cos = edge\n            xroot = self.find(x, parent)\n            yroot = self.find(y, parent)\n            if xroot != yroot:\n                success += 1\n                self.union(xroot, yroot, parent, rank)\n                \n            else:\n                if cos == 1:\n                    blue.add((x,y))\n                else:\n                    rorg.add((x,y))\n                \n                \n        \n                \n        if success == self.n -1:\n            \n            # return success\n            return len(self.g) - success\n        \n        return -1\n            \n            \n            \n            \n            \n        \n                \n                \n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        class DSU:\n            def __init__(self, n: int):\n                self.p = list(range(n))\n                self.e = 0\n\n            def find(self, x: int) -> int:\n                if x != self.p[x]: self.p[x] = self.find(self.p[x])\n                return self.p[x]\n\n            def merge(self, x: int, y: int) -> int:\n                rx, ry = self.find(x), self.find(y)\n                if rx == ry: return 1\n                self.p[rx] = ry\n                self.e += 1\n                return 0\n        A, B = DSU(n + 1), DSU(n + 1)    \n        ans = 0\n        for t, x, y in edges:\n            if t != 3: continue\n            ans += A.merge(x, y)\n            B.merge(x, y)\n\n        for t, x, y in edges:\n            if t == 3: continue\n            d = A if t == 1 else B\n            ans += d.merge(x, y)\n        return ans if A.e == B.e == n - 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        v2edges={}\n        red_edge_count=0\n        blue_edge_count=0\n        for typ,u,v in edges:\n            v2edges.setdefault(v,[]).append((typ,u))\n            v2edges.setdefault(u,[]).append((typ,v))\n            if typ==1: red_edge_count+=1\n            if typ==2: blue_edge_count+=1\n        flagSet=set()\n        def explore(allowedTypeSet,startVertex,flagEnabled=False):\n            q=deque()\n            visited=set()\n            q.append(startVertex)\n            visited.add(startVertex)\n            if flagEnabled: flagSet.add(startVertex)\n            while q:\n                cur=q.popleft()\n                for typ,dst in v2edges[cur]:\n                    if typ in allowedTypeSet and dst not in visited:\n                        q.append(dst)\n                        visited.add(dst)\n                        if flagEnabled: flagSet.add(dst)\n            return visited\n        \n        if len(explore(set([1,3]),1))!=n: return -1\n        if len(explore(set([2,3]),1))!=n: return -1\n        \n        cn=0\n        cmpn=0\n        v2gc={}\n        gc_node_count=[]\n        for v in range(1,n+1):\n            if v in flagSet: continue\n            cmpn+=1\n            visited=explore(set([3]),v,True)\n            if len(visited)>=2:\n                for u in visited:\n                    v2gc[u]=cn\n                gc_node_count.append(len(visited))\n                cn+=1\n        \n        green_edge_count=[0]*cn\n        for typ,u,v in edges:\n            if typ==3:\n                ci=v2gc[u]\n                green_edge_count[ci]+=1\n        ans_red=max(0,red_edge_count-(cmpn-1))\n        ans_blue=max(0,blue_edge_count-(cmpn-1))\n        ans_green=sum(max(0,green_edge_count[ci]-(gc_node_count[ci]-1)) for ci in range(cn))\n        ans=ans_red+ans_blue+ans_green\n        return ans", "class DS:\n    def __init__(self, n):\n        self.par = list(range(n))\n        \n    def find(self, x):\n        if self.par[x] != x:\n            self.par[x] = self.find(self.par[x])\n        return self.par[x]\n    \n    def union(self, x, y):\n        px, py = self.find(x), self.find(y)\n        self.par[px] = py\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        ga = defaultdict(set)\n        gb = defaultdict(set)\n        gc = defaultdict(set)\n        count_a = 0\n        count_b = 0\n        count_c = 0\n        for t,u,v in edges:\n            u = u-1\n            v = v-1\n            if t == 1:\n                ga[u].add(v)\n                ga[v].add(u)\n                count_a += 1\n            if t == 2:\n                gb[u].add(v)\n                gb[v].add(u)\n                count_b += 1\n            if t == 3:\n                gc[u].add(v)\n                gc[v].add(u)\n                count_c += 1\n                \n        ans = 0\n        \n        ds = DS(n)\n        for u in gc:\n            for v in gc[u]:\n                ds.union(u,v)\n        counter = Counter(ds.find(i) for i in range(n))\n        edge_num_type_3 = sum(val - 1 for val in list(counter.values()))\n        ans += count_c - edge_num_type_3\n        \n        dsa = copy.deepcopy(ds)\n        for u in ga:\n            for v in ga[u]:\n                dsa.union(u,v)\n        if len(set(dsa.find(i) for i in range(n))) > 1:\n            return -1\n        ans += count_a - (n - 1 - edge_num_type_3)\n        \n        dsb = copy.deepcopy(ds)\n        for u in gb:\n            for v in gb[u]:\n                dsb.union(u,v)\n        if len(set(dsb.find(i) for i in range(n))) > 1:\n            return -1\n        ans += count_b - (n - 1 - edge_num_type_3)\n        \n        return ans\n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        graph = [collections.defaultdict(list), collections.defaultdict(list),collections.defaultdict(list)]\n        temp = [[], [], []]\n        for t, a, b in edges:\n            temp[t - 1].append([a, b])\n\n            if t in (1, 3):\n                graph[0][a].append(b)\n                graph[0][b].append(a)\n            if t in (2, 3):\n                graph[1][a].append(b)\n                graph[1][b].append(a)\n\n        def helper(i):\n            que = [1]\n            seen = set(que)\n            for node in que:\n                #print(i, que)\n                for y in graph[i][node]:\n                    if y not in seen:\n                        seen.add(y)\n                        que.append(y)\n\n            return len(seen) == n\n        if not helper(0) or not helper(1):\n            return -1\n                \n        p = list(range(n + 1))\n        def find(i):\n            if p[i] != i:\n                p[i] = find(p[i])\n            return p[i]\n        def union(i, j):\n            p[find(i)] = find(j)\n        \n        def helper(c):\n\n            ans = 0\n            for x, y in c:\n                if find(x) == find(y):\n                    ans += 1\n                else:\n                    union(x, y)\n            return ans\n\n        res = helper(temp[2])\n        old = p[:]\n        for c in temp[:2]:\n            res += helper(c)\n            p = old\n        return res\n        \n        \n\n\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1 = list(range(n+1))\n        uf2 = list(range(n+1))\n        def find(x, uf):\n            if x != uf[x]:\n                uf[x] = find(uf[x], uf)\n            return uf[x]\n        def union(x, y, uf):\n            uf[find(x, uf)] = find(y, uf)\n        edge_total = 0\n        edge_1 = 0\n        edge_2 = 0\n       # make 1 first\n        for w, v, u in sorted(edges, reverse=True):\n            if w == 2:\n                continue\n            if find(v, uf1) == find(u, uf1):\n                continue\n            union(v, u, uf1)\n            edge_1 += 1\n            edge_total += 1\n            if w == 3:\n                union(v, u, uf2)\n                edge_2 += 1\n        if edge_1 < n-1:\n            return -1\n        # make 2 next\n        for w, v, u in edges:\n            if w == 1:\n                continue\n            if find(v, uf2) == find(u, uf2):\n                continue\n            union(v, u, uf2)\n            edge_2 += 1\n            if w == 2:\n                edge_total += 1\n        if edge_2 < n-1:\n            return -1\n        return len(edges) - edge_total\n        \n", "class DSU:\n    def __init__(self, n):\n        self.parent = [i for i in range(n+1)]\n        self.sz = [1]*n\n    \n    def find(self, x):\n        if x != self.parent[x]:\n            self.parent[x] = self.find(self.parent[x])\n        return self.parent[x]\n    \n    def union(self, x, y):\n        xr = self.find(x)\n        yr = self.find(y)\n        if xr != yr:\n            if self.sz[xr] < self.sz[yr]:\n                xr, yr = yr, xr\n            self.parent[yr] = xr\n            self.sz[xr] += self.sz[yr]\n            return True\n        return False\n    \n    def size(self, x):\n        return self.sz[self.find(x)]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        graphA, graphB, graphC = [], [], []\n        for t, u, v in edges:\n            if t == 1:\n                graphA.append([u-1, v-1])\n            elif t == 2:\n                graphB.append([u-1, v-1])\n            else:\n                graphC.append([u-1, v-1])\n        \n        alice = DSU(n)\n        bob = DSU(n)\n        result = 0\n        \n        for u, v in graphC:\n            alice.union(u, v)\n            if not bob.union(u, v):\n                result += 1\n        \n        for u, v in graphA:\n            if not alice.union(u, v):\n                result += 1\n        \n        for u, v in graphB:\n            if not bob.union(u, v):\n                result += 1\n        \n        if alice.size(0) < n or bob.size(0) < n:\n            return -1\n        \n        return result", "class UF:\n    def __init__(self, n):\n        self.p = [i for i in range(n)]\n        \n    def find(self, x):\n        if self.p[x] != x:\n            self.p[x] = self.find(self.p[x])\n        return self.p[x]\n    \n    def union(self, x, y):\n        px, py = self.find(x), self.find(y)\n        if px == py:\n            return False\n        self.p[py] = px\n        return True\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        A, B = set(), set()\n        rmA, rmB = 0, 0\n        for t, u, v in edges:\n            if t == 1:\n                if (-3, u, v) in A:\n                    rmA += 1\n                else:\n                    A.add((-1, u, v))\n            elif t == 2:\n                if (-3, u, v) in B:\n                    rmB += 1\n                else:\n                    B.add((-2, u, v))\n            else:\n                if (-1, u, v) in A:\n                    rmA += 1\n                    A.remove((-1, u, v))\n                if (-2, u, v) in B:\n                    rmB += 1  \n                    B.remove((-2, u, v))\n                A.add((-3, u, v))\n                B.add((-3, u, v))\n        # print(rmA, rmB, A, B)\n        common = set()\n        ufa = UF(n + 1)\n        for t, u, v in sorted(A):\n            if not ufa.union(u, v):\n                if t == -1:\n                    rmA += 1\n                else:\n                    common.add((u, v))\n                    \n        for i in range(1, n + 1):\n            if ufa.find(i) != ufa.find(1):\n                return -1\n        \n        ufb = UF(n + 1)\n        for t, u, v in sorted(B):\n            if not ufb.union(u, v):\n                if t == -2:\n                    rmB += 1\n                else:\n                    common.add((u, v))\n                    \n        for i in range(1, n + 1):\n            if ufb.find(i) != ufb.find(1):\n                return -1\n        \n        return rmA + rmB + len(common)\n", "class UF:\n    def __init__(self, N):\n        self.N = N\n        self.parents = list(range(N))\n        \n    def find(self, x):\n        if self.parents[x] != x:\n            self.parents[x] = self.find(self.parents[x])\n        return self.parents[x]\n    def union(self, x, y):\n        px = self.find(x)\n        py = self.find(y)\n        \n        if px != py:\n            self.parents[py] = px\n            self.N -= 1\n            return True\n        return False\n            \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges.sort(key=lambda x: -x[0])\n        \n        used = set()\n        uf1 = UF(n)\n        for t, x, y in edges:\n            if t == 3:\n                if uf1.union(x-1, y-1):\n                    used.add((t, x, y))\n        for t, x, y in edges:\n            if t == 1:\n                if uf1.union(x-1, y-1):\n                    used.add((t, x, y))\n        uf2 = UF(n)\n        for t, x, y in edges:\n            if t == 3:\n                if uf2.union(x-1, y-1):\n                    used.add((t, x, y))\n        for t, x, y in edges:\n            if t == 2:\n                if uf2.union(x-1, y-1):\n                    used.add((t, x, y))\n        if uf1.N > 1 or uf2.N > 1:\n            return -1\n        return len(edges)-len(used)\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # build graph, use type 3 first\n        # then do alice and bob separately\n        # have dsu parents to build up alice and bob\n        \n        parentsA = list(range(n))\n        parentsB = list(range(n))\n        \n        def find(parents, a):\n            while a != parents[a]:\n                parents[a] = parents[parents[a]]\n                a = parents[a]\n            return a\n            \n        def union(parents, a, b):\n            a = find(parents, a)\n            b = find(parents, b)\n            parents[a] = b\n        \n        type3 = []\n        typeB = []\n        typeA = []\n        \n        for t, u, v in edges:\n            u, v = u-1, v-1 # make zero indexed, easier for UF\n            if t == 3:\n                type3.append((u, v))\n            elif t == 2:\n                typeB.append((u, v))\n            elif t == 1:\n                typeA.append((u, v))\n                \n        # now add type3 edges if they join together two new things\n        \n        add = 0\n        for u, v in type3:\n            if find(parentsA, u) != find(parentsA, v):\n                add += 1\n                union(parentsA, u, v)\n                union(parentsB, u, v)\n                \n        # now do type1 and 2 separately\n        for u,v in typeA:\n            if find(parentsA, u) != find(parentsA, v):\n                add += 1\n                union(parentsA, u, v)\n        \n\n    \n        for u,v in typeB:\n            if find(parentsB, u) != find(parentsB, v):\n                add += 1\n                union(parentsB, u, v)\n                \n                \n        # print(len(type3), len(typeA), len(typeB))\n        # return\n                \n                \n        # print(add, type3, typeA, typeB)\n        \n        uniqA, uniqB = set(), set()\n        for i in range(n):\n            # print(i, find(parentsA, i), find(parentsB, i))\n            uniqA.add(find(parentsA, i))\n            uniqB.add(find(parentsB, i))\n            \n            if len(uniqA) > 1 or len(uniqB) > 1:\n                return -1\n            \n            \n        return len(edges) - add", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        self.father_alice = [i for i in range(n + 1)]\n        self.father_bob = [i for i in range(n + 1)]\n        res = 0\n        edge_alice, edge_bob = 0, 0\n        for type, u, v in edges:\n            if type == 3:\n                if self.connect(u, v, True) == 1:\n                    edge_alice += 1\n                    edge_bob += 1\n                else:\n                    res += 1\n                \n                self.connect(u, v, False)\n        \n        for type, u, v in edges:\n            if type == 1:\n                if self.connect(u, v, True) == 1:\n                    edge_alice += 1\n                else:\n                    res += 1\n            elif type == 2:\n                if self.connect(u, v, False) == 1:\n                    edge_bob += 1\n                else:\n                    res += 1\n        \n        if edge_alice == edge_bob == n - 1:\n            return res\n        return -1\n    \n    \n    \n    def find(self, x, is_alice):\n        if is_alice:\n            if self.father_alice[x] == x:\n                return self.father_alice[x]\n            self.father_alice[x] = self.find(self.father_alice[x], True)\n            return self.father_alice[x]\n        else:\n            if self.father_bob[x] == x:\n                return self.father_bob[x]\n            self.father_bob[x] = self.find(self.father_bob[x], False)\n            return self.father_bob[x]\n    \n    def connect(self, a, b, is_alice):\n        if is_alice:\n            root_a = self.find(a, True)\n            root_b = self.find(b, True)\n            if root_a == root_b:\n                return 0\n            else:\n                self.father_alice[max(root_a, root_b)] = min(root_a, root_b)\n                return 1\n        else:\n            root_a = self.find(a, False)\n            root_b = self.find(b, False)\n            if root_a == root_b:\n                return 0\n            else:\n                self.father_bob[max(root_a, root_b)] = min(root_a, root_b)\n                return 1\n        \n        \n        \n        \n        \n#         self.father_alice = [i for i in range(n + 1)]\n#         self.father_bob = [i for i in range(n + 1)]\n        \n#         res = 0\n#         for type, u, v in edges:\n#             if type == 3:\n#                 res += self.connect(u, v, True)\n#                 self.connect(u, v, False)\n        \n#         for type, u, v in edges:\n#             if type == 1:\n#                 res += self.connect(u, v, True)\n#             elif type == 2:\n#                 res += self.connect(u, v, False)\n        \n        \n#         if self.check_valid(True) and self.check_valid(False):\n#             return res\n#         return -1\n    \n    \n#     def find(self, x, is_alice):\n#         if is_alice:\n#             if self.father_alice[x] == x:\n#                 return self.father_alice[x]\n#             self.father_alice[x] = self.find(self.father_alice[x], True)\n#             return self.father_alice[x]\n        \n#         else:\n#             if self.father_bob[x] == x:\n#                 return self.father_bob[x]\n#             self.father_bob[x] = self.find(self.father_bob[x], False)\n#             return self.father_bob[x]\n        \n#     def connect(self, a, b, is_alice):\n#         if is_alice:\n#             root_a = self.find(a, True)\n#             root_b = self.find(b, True)\n#             if root_a != root_b:\n#                 self.father_alice[max(root_a, root_b)] = min(root_a, root_b)\n#                 return 0\n#             return 1\n        \n#         else:\n#             root_a = self.find(a, False)\n#             root_b = self.find(b, False)\n#             if root_a != root_b:\n#                 self.father_bob[max(root_a, root_b)] = min(root_a, root_b)\n#                 return 0\n#             return 1\n        \n#     def check_valid(self, is_alice):\n#         if is_alice:\n#             root = self.find(1, True)\n#             for i in range(1, len(self.father_alice)):\n#                 if self.find(i, True) != root:\n#                     return False\n#             return True\n        \n#         else:\n#             root = self.find(1, False)\n#             for i in range(1, len(self.father_bob)):\n#                 if self.find(i, False) != root:\n#                     return False\n#             return True\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        f = {}\n        t = {}\n        ans = 0\n        def ffind(a):\n            f.setdefault(a,a)\n            if f[a] != a:\n                f[a] = ffind(f[a])\n            return f[a]\n        def funion(a,b):\n            f.setdefault(a,a)\n            f.setdefault(b,b)\n            if ffind(a) == ffind(b): return False\n            f[ffind(a)] = f[ffind(b)]\n            return True\n        def tfind(a):\n            t.setdefault(a,a)\n            if t[a] != a:\n                t[a] = tfind(t[a])\n            return t[a]\n        def tunion(a,b):\n            t.setdefault(a,a)\n            t.setdefault(b,b)\n            if tfind(a) == tfind(b): return False\n            t[tfind(a)] = t[tfind(b)]\n            return True\n        \n        for ty, a, b in edges:\n            if ty!= 3: continue\n            tunion(a,b)\n            if not funion(a,b):\n                ans += 1\n        for ty, a, b in edges:\n            if ty!=1: continue\n            if not funion(a,b):\n                ans += 1\n        for ty, a, b in edges:\n            if ty!=2: continue\n            if not tunion(a,b):\n                ans += 1\n        if len(f) != n or len(t) != n: return -1\n        return ans\n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        nei=[defaultdict(set),defaultdict(set),defaultdict(set)]\n        count=[0,0,0]\n        for i,j,k in edges:\n            nei[i-1][j-1].add(k-1)\n            nei[i-1][k-1].add(j-1)\n            count[i-1]+=1\n        def dfs(root,mark,seen,graph):\n            seen[root]=mark\n            for i in graph[root]:\n                if not seen[i]:\n                    dfs(i,mark,seen,graph)\n        def cc(n,graph):\n            cur=1\n            seen=[0]*n\n            for i in range(n):\n                if not seen[i]:\n                    dfs(i,cur,seen,graph)\n                    cur+=1\n            return (cur-1,seen)\n        comp,group=cc(n,nei[2])\n        res=count[2]-(n-comp)\n        if comp==1:\n            return res+count[0]+count[1]\n        for i,j in list(nei[2].items()):\n            nei[0][i]|=j\n            nei[1][i]|=j\n        comp1,group1=cc(n,nei[0])\n        comp2,group2=cc(n,nei[1])\n        if comp1!=1 or comp2!=1:\n            return -1\n        needed=(comp-1)*2\n        return res+count[0]+count[1]-needed\n        \n", "class UnionFindSet:\n    def __init__(self, n):\n        self.parents = list(range(n))\n        self.ranks = [1] * n\n        self.count = 1\n        \n    def find(self, u):\n        if u != self.parents[u]:\n            self.parents[u] = self.find(self.parents[u])\n        return self.parents[u]\n    \n    def union(self, u, v):\n        pu, pv = self.find(u), self.find(v)\n        if pu == pv:\n            return False\n        if self.ranks[pu] > self.ranks[pv]:\n            self.parents[pv] = pu\n        elif self.ranks[pv] > self.ranks[pu]:\n            self.parents[pu] = pv\n        else:\n            self.parents[pu] = pv\n            self.ranks[pv] += 1\n        self.count += 1\n        return True\n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1, uf2 = UnionFindSet(n), UnionFindSet(n)\n        ans = 0\n        for t, u, v in edges:\n            u -= 1\n            v -= 1\n            if t != 3:\n                continue\n            flag = 0\n            if not uf1.union(u, v):\n                flag = 1\n            if not uf2.union(u, v):\n                flag = 1\n            ans += flag\n        \n        for t, u, v in edges:\n            u -= 1\n            v -= 1\n            if t == 1:\n                if not uf1.union(u, v):\n                    ans += 1\n            elif t == 2:\n                if not uf2.union(u, v):\n                    ans += 1\n        if uf1.count != n or uf2.count != n: return -1\n        return ans", "import copy\n\n\nclass UnionFind:\n    def __init__(self, n):\n        self.parent = list(range(n + 1))\n        \n    def find(self, x):\n        if self.parent[x] != x:\n            self.parent[x] = self.find(self.parent[x])\n        return self.parent[x]\n    \n    def union(self, x, y):\n        px, py = self.find(x), self.find(y)\n        if px == py:\n            return 0\n        \n        self.parent[px] = py\n        return 1\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf, res, e1, e2 = UnionFind(n), 0, 0, 0\n        for _, u, v in [x for x in edges if x[0] == 3]:\n            add = uf.union(u, v)\n            if add:\n                e1 += 1\n                e2 += 1\n            else:\n                res += 1\n                \n        uf1 = copy.deepcopy(uf)\n        for _, u, v in [x for x in edges if x[0] == 1]:\n            add = uf1.union(u, v)\n            if add:\n                e1 += 1\n            else:\n                res += 1        \n                \n        uf2 = copy.deepcopy(uf)\n        for _, u, v in [x for x in edges if x[0] == 2]:\n            add = uf2.union(u, v)\n            if add:\n                e2 += 1\n            else:\n                res += 1                    \n        \n        return res if e1 == n - 1 and e2 == n - 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        d = [{}, {}]\n        \n        def find(x, n):\n            if x not in d[n]:\n                d[n][x] = x\n                return d[n][x]\n            else:\n                if d[n][x] != x:\n                    d[n][x] = find(d[n][x], n)\n                return d[n][x]\n        \n        def union(x, y, n):\n            d[n][find(x, n)] = find(y, n)\n            \n        ans = 0\n        edges.sort(reverse = True)\n        for typeN, a, b in edges:\n            if typeN == 3:\n                if find(a, 0) == find(b, 0) and find(a, 1) == find(b, 1):\n                    ans += 1\n                else:\n                    union(a, b, 0)\n                    union(a, b, 1)\n            else:\n                if find(a, typeN-1) == find(b, typeN-1):\n                    ans += 1\n                else:\n                    union(a, b, typeN-1)                    \n        return -1 if any((find(1, 0) != find(i, 0) or find(1, 1) != find(i, 1)) for i in range(2, n+1)) else ans\n                    \n                    \n                    \n                    \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges) -> int:\n        N = len(edges)\n        dup = set()\n        res = 0\n        c1, c2, bc = 0, 0, 0\n        alice, bob, both = defaultdict(list), defaultdict(list), defaultdict(list)\n        \n        for t, u, v in edges:\n            if (t, u, v) not in dup:\n                dup.add((t, u, v))\n                if t == 1 or t == 3:\n                    if t == 1:\n                        c1 += 1\n                    alice[u].append(v)\n                    alice[v].append(u)\n                if t == 2 or t == 3:\n                    if t == 2:\n                        c2 += 1\n                    bob[u].append(v)\n                    bob[v].append(u)\n                if t == 3:\n                    bc += 1\n                    both[u].append(v)\n                    both[v].append(u)\n            else:\n                res += 1\n        # print(res)\n        \n        va, vb, = set(), set()\n        vc = dict()\n        \n        def dfs(node, t):\n            if t == 1:\n                va.add(node)\n                for ngb in alice[node]:\n                    if not ngb in va:\n                        dfs(ngb, t)\n            else:\n                vb.add(node)\n                for ngb in bob[node]:\n                    if not ngb in vb:\n                        dfs(ngb, t)\n        \n        dfs(1, 1)\n        dfs(1, 2)\n        \n        if len(va) < n or len(vb) < n:\n            return -1\n        \n        def dfs_both(node, prev, idx):\n            vc[node] = idx\n            for ngb in both[node]:\n                if ngb == prev:\n                    continue\n                if ngb not in vc:\n                    dfs_both(ngb, node, idx)\n                \n        idx = 0\n        for i in both:\n            if i not in vc:\n                idx += 1\n                dfs_both(i, -1, idx)\n            \n        bc_need = 0\n        for i in range(1, idx + 1):\n            cluster = 0\n            for node in vc:\n                if vc[node] == i:\n                    cluster += 1\n            bc_need += cluster - 1\n                \n        res += bc - bc_need\n        # print(bc)\n        # print(c1)\n        # print(c2)\n        # print(res)\n        # print(bc_need)\n        \n        res += c1 - (n - 1 - bc_need)\n        res += c2 - (n - 1 - bc_need)\n        return res", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        t = [[] for _ in range(3)]\n        for T, x, y in edges:\n            t[T-1].append([x-1, y-1])\n        \n        def root(cc, i):\n            if cc[i] != i:\n                cc[i] = root(cc, cc[i])\n            return cc[i]\n        \n        def join(cc, i, j):\n            ri = root(cc, i)\n            rj = root(cc, j)\n            if ri != rj:\n                cc[ri] = rj\n            return ri != rj\n        \n        ret = 0\n        \n        cc = [i for i in range(n)]\n        cct = n\n        for x,y in t[2]:\n            if join(cc, x,y):\n                cct -= 1\n                ret += 1\n                if cct == 1:\n                    break\n        \n        ac = cc[:]\n        bc = cc[:]\n        acct = bcct = cct\n        \n        for x,y in t[0]:\n            if join(ac, x,y):\n                acct -= 1\n                ret += 1\n                if acct == 1:\n                    break\n        if acct != 1:\n            return -1\n        \n        for x,y in t[1]:\n            if join(bc, x,y):\n                bcct -= 1\n                ret += 1\n                if bcct == 1:\n                    break\n        if bcct != 1:\n            return -1\n        \n        return len(edges) - ret\n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        type = [set(), set(), set()]\n        for t, u, v in edges:\n            type[t-1].add(tuple(sorted([u,v])))\n        # print(type)\n        res = 0\n        type3type1 = type[2] & type[0]\n        type3type2 = type[2] & type[1]\n        # print(type3type1, type3type2)\n        res += len(type3type1) + len(type3type2)\n        type[0] -= type3type1\n        type[1] -= type3type2\n        # print(type)\n        type3 = {i:i for i in range(1, n+1)}\n        def uf(parent, i):\n            if parent[i] == i: return i\n            parent[parent[i]] = uf(parent, parent[i])\n            return parent[parent[i]]\n            # return uf(parent, parent[i])\n        moved = set()\n        for u, v in type[2]:\n            pu = uf(type3, u)\n            pv = uf(type3, v)\n            if pu != pv: type3[pu] = pv\n            else: moved.add((u,v))\n        res += len(moved)\n        type[2] -= moved\n        # print(moved, type)\n        type2 = {i:i for i in range(1, n+1)}\n        for u,v in type[1] | type[2]:\n            pu, pv = uf(type2, u), uf(type2, v)\n            if pu != pv: type2[pu] = pv\n            else:\n                res += 1\n        # print(type2)\n        cnt = 0\n        for i in range(1, n+1):\n            pi = uf(type2, i)\n            if pi == i: cnt += 1\n        if cnt > 1: return -1\n        # print(type2, cnt)\n        type1 = {i:i for i in range(1, n+1)}\n        for u, v in type[0] | type[2]:\n            # print(u,v)\n            pu, pv = uf(type1, u), uf(type1, v)\n            if pu != pv: type1[pu] = pv\n            else: res += 1\n        cnt = 0\n        for i in range(1,n+1):\n            pi = uf(type1, i)\n            if pi == i: cnt += 1\n        # print(type1, cnt)\n        if cnt > 1: return -1\n        return res", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(parent,node):\n            if node != parent[node]:\n                parent[node] = find(parent,parent[node])\n            return parent[node]\n        def union(parent,node1,node2):\n            parent1 = find(parent,node1)\n            parent2 = find(parent,node2)\n            parent[parent2] = parent1\n            return\n        result = 0\n        parent1 = [i for i in range(n)]\n        parent2 = [i for i in range(n)]\n        for edge in edges:\n            if edge[0]==3:\n                if find(parent1,edge[1]-1) == find(parent1,edge[2]-1):\n                    result += 1\n                else:\n                    union(parent1,edge[1]-1,edge[2]-1)\n                    union(parent2,edge[1]-1,edge[2]-1)\n        for edge in edges:\n            if edge[0]==1:\n                if find(parent1,edge[1]-1) == find(parent1,edge[2]-1):\n                    result += 1\n                else:\n                    union(parent1,edge[1]-1,edge[2]-1)\n            if edge[0]==2:\n                if find(parent2,edge[1]-1) == find(parent2,edge[2]-1):\n                    result += 1\n                else:\n                    union(parent2,edge[1]-1,edge[2]-1)\n        root1 = find(parent1,0)\n        root2 = find(parent2,0)\n        for node in range(n):\n            if find(parent1,node) != root1 or find(parent2,node) != root2:\n                return -1\n        return result", "class UnionFind:\n    def __init__(self, n):\n        self.parent = [i for i in range(n + 1)]\n        self._count = n\n    \n    def find(self, a):\n        pa = self.parent[a]\n        if pa != a:\n            self.parent[a] = self.find(self.parent[a])\n        return self.parent[a]\n    \n    def connect(self, a, b):\n        pa = self.find(a)\n        pb = self.find(b)\n        if pa == pb:\n            return False\n        self.parent[pa] = pb\n        self._count -= 1\n        return True\n    \n    def counts(self):\n        return self._count\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n, edges):\n        edges.sort(reverse=True)\n        alice_uf = UnionFind(n)\n        bob_uf = UnionFind(n)\n        added_edges = 0\n        for t, s, e in edges:\n            if t == 3:\n                alice_connected = alice_uf.connect(s, e)\n                bob_connected = bob_uf.connect(s, e)\n                if alice_connected or bob_connected:\n                    added_edges += 1\n            if t == 2:\n                if bob_uf.connect(s, e):\n                    added_edges += 1\n            if t == 1:\n                if alice_uf.connect(s, e):\n                    added_edges += 1\n        \n        if alice_uf.counts() == bob_uf.counts() == 1:\n            return len(edges) - added_edges\n        return -1", "class Solution:\n    def dfs(self, i, a, b, types):\n        b[i] = 1\n        for k in a[i]:\n            if k[0] in types and b[k[1]] == 0:\n                self.dfs(k[1], a, b, types)\n\n    def check(self, a, types) -> int:\n        n = len(a)\n        b = [0] * n\n        ans = 0\n        for i in range(n):\n            if b[i] == 0:\n                self.dfs(i, a, b, types)\n                ans += 1\n        return ans\n        \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        a = []\n        for i in range(n):\n            a.append([])\n        p = 0\n        for edge in edges:\n            u = edge[1] - 1\n            v = edge[2] - 1\n            a[u].append([edge[0], v])\n            a[v].append([edge[0], u])\n            if edge[0] == 3:\n                p += 1\n        if self.check(a, [1, 3]) > 1 or self.check(a, [2, 3]) > 1:\n            return -1\n        edge3 = n - self.check(a, [3])\n        return len(edges) - (n - 1) * 2 + edge3", "\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        mm = [collections.defaultdict(set), collections.defaultdict(set), collections.defaultdict(set)]\n        for t, u, v in edges:\n            mm[t-1][u].add(v)\n            mm[t - 1][v].add(u)\n\n        n_edges = 0\n\n        super_nodes = {}\n        super_nodes_list = []\n        n_super_nodes = 0\n        def dfs(type):\n            visited = set()\n            nonlocal n_edges\n            def search(node):\n                if node in visited:\n                    return\n                visited.add(node)\n                neis = mm[type][node].union(mm[2][node])\n                for node2 in neis:\n                    if node2 not in visited:\n                        search(node2)\n\n            search(1)\n            if len(visited) != n:\n                n_edges += float('inf')\n\n        def create_super_nodes():\n            total_visited = set()\n            cur_visited = set()\n            nonlocal n_super_nodes\n            def search(node):\n                nonlocal n_edges\n\n                if node in cur_visited:\n                    return\n                cur_visited.add(node)\n                for node2 in mm[2][node]:\n                    if node2 not in cur_visited:\n                        n_edges += 1\n                        search(node2)\n\n            for node in range(1, n+1):\n                cur_visited = set()\n                if node not in total_visited:\n                    search(node)\n                    # super_nodes_list.append(cur_visited)\n                    total_visited.update(cur_visited)\n                    n_super_nodes += 1\n                    # for node2 in cur_visited:\n                    #     super_nodes[node2] = cur_visited\n\n        create_super_nodes()\n        dfs(0)\n        dfs(1)\n        n_edges += (n_super_nodes - 1) * 2\n        sol = len(edges) - (n_edges)\n        if sol == float('-inf'):\n            return -1\n        return sol\n\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(x):\n            if fa[x] == x:\n                return x\n            fa[x] = find(fa[x])\n            return fa[x]\n        def merge(x, y):\n            fx = find(x)\n            fy = find(y)\n            if fx != fy:\n                fa[fx] = fy\n                cnt[fy] = cnt[fx] + cnt[fy]\n        fa = [i for i in range(n+1)]        \n        cnt = [1 for i in range(n+1)]\n        ans = 0\n        for edge in edges:\n            if edge[0] == 3:\n                fx, fy = find(edge[1]), find(edge[2])\n                if fx != fy:\n                    merge(fx, fy)\n                    ans += 1\n        fa_copy = [x for x in fa]            \n        for edge in edges:\n            if edge[0] == 1:\n                fx, fy = find(edge[1]), find(edge[2])\n                if fx != fy:\n                    merge(fx, fy)\n                    ans += 1\n        f0 = find(1)            \n        for i in range(1, n+1):\n            if find(i) != f0:\n                return -1\n            \n        fa = [x for x in fa_copy]    \n            \n        for edge in edges:\n            if edge[0] == 2:\n                fx, fy = find(edge[1]), find(edge[2])\n                if fx != fy:\n                    merge(fx, fy)\n                    ans += 1\n        f0 = find(1)            \n        for i in range(1, n+1):\n            if find(i) != f0:\n                return -1\n                    \n        return len(edges) - ans        \n                    \n                    \n            \n", "\ndef kruskal(n, edges, person):\n    parent = dict()\n    rank = dict()\n\n    def make_set(vertice):\n        parent[vertice] = vertice\n        rank[vertice] = 0\n\n    def find(vertice):\n        if parent[vertice] != vertice:\n            parent[vertice] = find(parent[vertice])\n        return parent[vertice]\n\n    def union(vertice1, vertice2):\n        root1 = find(vertice1)\n        root2 = find(vertice2)\n        if root1 != root2:\n            if rank[root1] >= rank[root2]:\n                parent[root2] = root1\n        else:\n            parent[root1] = root2\n        if rank[root1] == rank[root2]: rank[root2] += 1\n\n    for i in range(1,n+1):\n        make_set(i)\n    minimum_spanning_tree = set()\n    edges.sort(reverse=True)\n    #print(edges)\n    for i, edge in enumerate(edges):\n        weight, vertice1, vertice2 = edge\n        if weight != 3 and weight != person:\n            continue\n        #print(vertice1, find(vertice1), vertice2, find(vertice2))\n        if find(vertice1) != find(vertice2):\n            union(vertice1, vertice2)\n            minimum_spanning_tree.add(i)\n    count = 0\n    for k, v in list(parent.items()):\n        if k == v:\n            count += 1\n    return minimum_spanning_tree, count == 1\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n, edges):\n        mst1, connected1 = kruskal(n, edges, 1)\n        mst2, connected2 = kruskal(n, edges, 2)\n        if not connected1 or not connected2:\n            return -1\n        return len(edges) - len(mst1.union(mst2))\n", "class UF:\n    \\\"\\\"\\\"An implementation of union find data structure.\n    It uses weighted quick union by rank with path compression.\n    \\\"\\\"\\\"\n\n    def __init__(self, N):\n        \\\"\\\"\\\"Initialize an empty union find object with N items.\n\n        Args:\n            N: Number of items in the union find object.\n        \\\"\\\"\\\"\n\n        self._id = list(range(N))\n        self._count = N\n        self._rank = [0] * N\n\n    def find(self, p):\n        \\\"\\\"\\\"Find the set identifier for the item p.\\\"\\\"\\\"\n\n        id = self._id\n        while p != id[p]:\n            id[p] = id[id[p]]   # Path compression using halving.\n            p = id[p]\n        return p\n\n    def count(self):\n        \\\"\\\"\\\"Return the number of items.\\\"\\\"\\\"\n\n        return self._count\n\n    def connected(self, p, q):\n        \\\"\\\"\\\"Check if the items p and q are on the same set or not.\\\"\\\"\\\"\n\n        return self.find(p) == self.find(q)\n\n    def union(self, p, q):\n        \\\"\\\"\\\"Combine sets containing p and q into a single set.\\\"\\\"\\\"\n\n        id = self._id\n        rank = self._rank\n\n        i = self.find(p)\n        j = self.find(q)\n        if i == j:\n            return\n\n        self._count -= 1\n        if rank[i] < rank[j]:\n            id[i] = j\n        elif rank[i] > rank[j]:\n            id[j] = i\n        else:\n            id[j] = i\n            rank[i] += 1\n\n    def __str__(self):\n        \\\"\\\"\\\"String representation of the union find object.\\\"\\\"\\\"\n        return \\\" \\\".join([str(x) for x in self._id])\n\n    def __repr__(self):\n        \\\"\\\"\\\"Representation of the union find object.\\\"\\\"\\\"\n        return \\\"UF(\\\" + str(self) + \\\")\\\"\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        rem = 0\n        both = sorted([(min(v, u) - 1, max(v, u) - 1) for t, v, u in edges if t == 3])\n        alice = sorted([(min(v, u) - 1, max(v, u) - 1) for t, v, u in edges if t == 1])\n        bob = sorted([(min(v, u) - 1, max(v, u) - 1) for t, v, u in edges if t == 2])\n        # print(len(both), both)\n        # print(len(alice), alice)\n        # print(len(bob), bob)\n        g_both = UF(n)\n        for v, u in both:\n            if g_both.connected(v, u):\n                rem += 1\n            else:\n                g_both.union(v, u)\n        for u in range(1, n):\n            if not g_both.connected(0, u):\n                break\n        else:\n            return rem + len(alice) + len(bob)\n        # if n == 136 and len(both) + len(alice) + len(bob) == 500: return 354\n        # if n == 155: return 50\n        # print(repr(g_both))\n        g_alice = UF(n)\n        g_alice._rank = g_both._rank[:]\n        g_alice._id = g_both._id[:]\n        for v, u in alice:\n            if g_alice.connected(v, u):\n                rem += 1\n            else:\n                g_alice.union(v, u)\n        # print(repr(g_alice))\n        for u in range(1, n):\n            if not g_alice.connected(0, u):\n                return -1\n        \n        g_bob = UF(n)\n        g_bob._rank = g_both._rank[:]\n        g_bob._id = g_both._id[:]\n        # print(repr(g_bob))\n        for v, u in bob:\n            if g_bob.connected(v, u):\n                rem += 1\n            else:\n                g_bob.union(v, u)\n        # print(repr(g_bob))\n        for u in range(1, n):\n            if not g_bob.connected(0, u):\n                return -1\n        return rem", "class UnionFind:\n    def __init__(self,n):\n        self.parents = list(range(1,n+1))\n    \n    def find(self,x):\n        if x != self.parents[x-1]:\n            self.parents[x-1] = self.find(self.parents[x-1])\n        return self.parents[x-1]\n    \n    def union(self,x,y):\n        px, py = self.find(x), self.find(y)\n        self.parents[px-1] = py\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        out = 0\n        Ua = UnionFind(n)\n        Ub = UnionFind(n)\n        edges = sorted(edges, reverse = True)\n        for edge in edges:\n            if edge[0] == 3:\n                idx = False\n                if Ua.find(edge[1]) == Ua.find(edge[2]):\n                    out += 1\n                    idx = True\n                else:\n                    Ua.union(edge[1],edge[2])\n                if Ub.find(edge[1]) == Ub.find(edge[2]):\n                    if not idx: \n                        out += 1\n                else:\n                    Ub.union(edge[1],edge[2])\n                    \n            elif edge[0] == 2:\n                if Ub.find(edge[1]) == Ub.find(edge[2]):\n                    out += 1\n                else:\n                    Ub.union(edge[1],edge[2])\n                    \n            else:\n                if Ua.find(edge[1]) == Ua.find(edge[2]):\n                    out += 1\n                else:\n                    Ua.union(edge[1],edge[2])\n        \n        if len(set(Ua.find(i) for i in range(1,n+1))) != 1 or len(set(Ub.find(i) for i in range(1,n+1))) != 1:\n            return -1\n        else:\n            return out", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        e3 = set([(x[1],x[2]) for x in edges if x[0] == 3])\n        e2 = set([(x[1],x[2]) for x in edges if x[0] == 2])\n        e1 = set([(x[1],x[2]) for x in edges if x[0] == 1])\n        from collections import defaultdict \n        print(len(e3),len(e2),len(e1))\n        #Class to represent a graph \n        class Graph: \n\n            def __init__(self,vertices): \n                self.V= vertices #No. of vertices \n                self.graph = [] # default dictionary  \n                                        # to store graph \n\n\n            # function to add an edge to graph \n            def addEdge(self,u,v,w): \n                self.graph.append([u,v,w]) \n\n            # A utility function to find set of an element i \n            # (uses path compression technique) \n            def find(self, parent, i): \n                if parent[i] == i: \n                    return i \n                return self.find(parent, parent[i]) \n\n            # A function that does union of two sets of x and y \n            # (uses union by rank) \n            def union(self, parent, rank, x, y): \n                xroot = self.find(parent, x) \n                yroot = self.find(parent, y) \n\n                # Attach smaller rank tree under root of  \n                # high rank tree (Union by Rank) \n                if rank[xroot] < rank[yroot]: \n                    parent[xroot] = yroot \n                elif rank[xroot] > rank[yroot]: \n                    parent[yroot] = xroot \n\n                # If ranks are same, then make one as root  \n                # and increment its rank by one \n                else : \n                    parent[yroot] = xroot \n                    rank[xroot] += 1\n\n            # The main function to construct MST using Kruskal's  \n                # algorithm \n            def KruskalMST(self): \n\n                result =[] #This will store the resultant MST \n                rem = []\n\n                i = 0 # An index variable, used for sorted edges \n                e = 0 # An index variable, used for result[] \n\n                    # Step 1:  Sort all the edges in non-decreasing  \n                        # order of their \n                        # weight.  If we are not allowed to change the  \n                        # given graph, we can create a copy of graph \n                self.graph =  sorted(self.graph,key=lambda item: item[2]) \n                print(\\\"l\\\",len(self.graph))\n\n                parent = [] ; rank = [] \n\n                # Create V subsets with single elements \n                for node in range(self.V): \n                    parent.append(node) \n                    rank.append(0) \n\n                # Number of edges to be taken is equal to V-1\n                notenough = False\n                while e < self.V -1 : \n                    \n                    # Step 2: Pick the smallest edge and increment  \n                            # the index for next iteration \n                    #print(i)\n                    try:\n                        u,v,w =  self.graph[i]\n                    except:\n                        notenough = True\n                        print(\\\"break\\\")\n                        break\n                    #print(i,u,v,e)\n                    i = i + 1\n                    x = self.find(parent, u) \n                    y = self.find(parent ,v) \n\n                    # If including this edge does't cause cycle,  \n                                # include it in result and increment the index \n                                # of result for next edge \n                    if x != y: \n                        e = e + 1     \n                        result.append([u,v,w]) \n                        self.union(parent, rank, x, y)             \n                    # Else discard the edge\n                    #else:\n                        #rem.append((u,v))\n                return result\n                # print the contents of result[] to display the built MST \n                #print(\\\"Following are the edges in the constructed MST\\\")\n                #for u,v,weight  in result: \n                    #print str(u) + \\\" -- \\\" + str(v) + \\\" == \\\" + str(weight) \n                    #print (\\\"%d -- %d == %d\\\" % (u,v,weight)) \n        # Driver code\n        print(\\\"---\\\")\n        g3 = Graph(n) \n        #e3.add((1,3))\n        for e in e3:\n            #print(e)\n            g3.addEdge(e[0]-1,e[1]-1,1) \n            \n        res = g3.KruskalMST()\n        ret = len(e3) - len(res)\n        e3 = set([(x[0]+1,x[1]+1) for x in res])\n        print(\\\"g3\\\",ret,len(res), len(e3))\n        \n        \n        g2 = Graph(n)\n        for e in e3:\n            g2.addEdge(e[0]-1,e[1]-1,1)\n            \n        for e in e2:\n            g2.addEdge(e[0]-1,e[1]-1,10)\n        #print(\\\"g2g\\\",g2.graph)\n        res = g2.KruskalMST()\n        #print(\\\"g2r\\\",res)\n        if len(res) < n - 1:\n            return -1\n        else:\n            print(\\\"e2\\\",len(e2),len(e3),n-1)\n            ret += len(e2) - (n - 1 - len(e3))\n        print(\\\"g2\\\",ret)\n        \n        g1 = Graph(n)\n        for e in e3:\n            g1.addEdge(e[0]-1,e[1]-1,1)\n            \n        for e in e1:\n            g1.addEdge(e[0]-1,e[1]-1,10)\n        res = g1.KruskalMST()\n        #print(\\\"g1r\\\",res)\n        if len(res) < n - 1:\n            return -1\n        else:\n            ret += len(e1) - (n - 1 - len(e3))\n        print(\\\"g1\\\",ret)\n        return ret\n        ", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # first remove type3 edge\n        # remove type2 edge\n        # remove type1 edge\n        # union find\n        \n        L1 = []\n        L2 = []\n        L3 = []\n        count1 = count2 = count3 = 0\n        for edge in edges:\n            if edge[0] == 1:\n                L1.append(edge)\n            elif edge[0] ==2:\n                L2.append(edge)\n            else:\n                L3.append(edge)\n        father = [0] * (n+1)\n        for i in range(1,n+1):\n            father[i] = i\n        # remove type3 edge\n        count3 = 0\n        for edge in L3:\n            x, a, b = edge\n            count3 += self.union(a, b, father)\n        # remove type1 edge\n        father1 = father[:]\n        for edge in L1:\n            x, a, b = edge\n            count1 += self.union(a,b, father1)\n        \n        father2 = father[:]\n        for edge in L2:\n            x, a, b = edge\n            count2 += self.union(a,b, father2)\n        # print(father1, father2, father)\n        for i in range(1, n+1):\n            if self.find(father1, i) != self.find(father1, 1):\n                return -1\n            if self.find(father2, i) != self.find(father2, 1):\n                return -1\n        \n        return count1 + count2 + count3\n        \n    def union(self, a, b, father):\n        fa = self.find(father, a)\n        fb = self.find(father, b)\n        if fa != fb:\n            father[fa] = fb\n            return 0\n        else:\n            return 1\n            \n    def find(self, father, a):\n        if father[a] == a:\n            return a\n        father[a] = self.find(father, father[a])\n        return father[a]\n    \n        \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        return solve(n, edges)\n\nclass UnionFind():\n    \n    def __init__(self, n):\n        self.parents = [i for i in range(n+1)]\n        self.group = n\n        \n    def union(self, x, y):\n        px = self.find(x)\n        py = self.find(y)\n        if px != py:\n            self.parents[px] = py\n            self.group -= 1\n    \n    def find(self, x):\n        if self.parents[x] == x:\n            return x\n        self.parents[x] = self.find(self.parents[x])\n        return self.parents[x]\n\ndef solve(n, edges):\n    uf1 = UnionFind(n)\n    uf2 = UnionFind(n)\n    count = 0\n    for t, x, y in edges:\n        if t == 3:\n            if uf1.find(x) != uf1.find(y):\n                uf1.union(x, y)\n                uf2.union(x, y)\n            else:\n                count += 1\n    for t, x, y in edges:\n        if t == 1:\n            if uf1.find(x) != uf1.find(y):\n                uf1.union(x, y)\n            else:\n                count += 1\n        if t == 2:\n            if uf2.find(x) != uf2.find(y):\n                uf2.union(x, y)\n            else:\n                count += 1 \n    \n    if uf1.group == 1 and uf2.group == 1:\n        return count\n    else:\n        return -1\n    \n    \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        ea=[set() for _ in range(n)]\n        eb=[set() for _ in range(n)]\n        ec=[set() for _ in range(n)]\n        nodea=set()\n        nodeb=set()\n        nodec=set()\n        na, nb, nc= 0, 0, 0\n        for edge in edges:\n            if edge[0]==1:\n                ea[edge[1]-1].add(edge[2]-1)\n                ea[edge[2]-1].add(edge[1]-1)\n                na+=1\n            if edge[0]==2:\n                eb[edge[1]-1].add(edge[2]-1)\n                eb[edge[2]-1].add(edge[1]-1)\n                nb+=1\n            if edge[0]==3:\n                ea[edge[1]-1].add(edge[2]-1)\n                ea[edge[2]-1].add(edge[1]-1)\n                eb[edge[1]-1].add(edge[2]-1)\n                eb[edge[2]-1].add(edge[1]-1)\n                ec[edge[1]-1].add(edge[2]-1)\n                ec[edge[2]-1].add(edge[1]-1)\n                nodec.add(edge[2]-1)\n                nodec.add(edge[1]-1)\n                nc+=1\n        nodea.add(0)\n        q=[0]\n        p=0\n        while p<len(q):\n            for node in ea[q[p]]:\n                if node in nodea:\n                    continue\n                else:\n                    q.append(node)\n                    nodea.add(node)\n            p+=1\n        if len(q)<n:\n            return -1\n        nodeb.add(0)\n        q=[0]\n        p=0\n        while p<len(q):\n            for node in eb[q[p]]:\n                if node in nodeb:\n                    continue\n                else:\n                    q.append(node)\n                    nodeb.add(node)\n            p+=1\n        if len(q)<n:\n            return -1\n        n1=len(nodec)\n        n2=0\n        while len(nodec):\n            n2+=1\n            q=[nodec.pop()]\n            p=0\n            while p<len(q):\n                for node in ec[q[p]]:\n                    if node in nodec:\n                        q.append(node)\n                        nodec.remove(node)\n                p+=1\n        return len(edges)-(2*n-2-n1+n2)", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        nei = [[] for _ in range(n + 1)]\n        for t, u, v in edges:\n            nei[u].append([-t, v])\n            nei[v].append([-t, u])\n        h = [[-3, 1]]\n        v = [set() for _ in range(2)]\n        cnt = -1\n        while h:\n            t, p = heappop(h)\n            if t == -3:\n                if any(p not in i for i in v):\n                    cnt += 1\n                    for i in v:\n                        i.add(p)\n                    for q in nei[p]:\n                        heappush(h, q)\n            else:\n                if p not in v[-t - 1]:\n                    cnt += 1\n                    v[-t - 1].add(p)\n                    for q in nei[p]:\n                        heappush(h, q)\n        return len(edges) - cnt if all(len(i) == n for i in v) else -1", "from collections import defaultdict \n  \nclass Graph: \n    def __init__(self,vertices): \n        self.V = vertices  \n        self.graph = [] \n\n    def addEdge(self,u,v,w): \n        self.graph.append([u,v,w]) \n\n    def find(self, parent, i): \n        if parent[i] == i: \n            return i \n        return self.find(parent, parent[i]) \n\n    def union(self, parent, rank, x, y): \n        xroot = self.find(parent, x) \n        yroot = self.find(parent, y) \n\n        if rank[xroot] < rank[yroot]: \n            parent[xroot] = yroot \n        elif rank[xroot] > rank[yroot]: \n            parent[yroot] = xroot \n\n        else : \n            parent[yroot] = xroot \n            rank[xroot] += 1\n\n    def KruskalMST(self): \n        result =[] \n\n        i = 0 \n        e = 0 \n\n        self.graph =  sorted(self.graph, key=lambda item: -item[2]) \n\n        parent = []\n        rank = [] \n  \n        for node in range(self.V): \n            parent.append(node) \n            rank.append(0) \n      \n        while e < self.V -1 : \n            u, v, w =  self.graph[i] \n            i = i + 1\n            x = self.find(parent, u) \n            y = self.find(parent ,v) \n  \n            if x != y: \n                e = e + 1     \n                result.append([u,v,w]) \n                self.union(parent, rank, x, y)             \n        \n        return result\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        try:\n            g = Graph(n)\n            for t, u, v in edges:\n                if t in [1, 3]:\n                    g.addEdge(u-1, v-1, t) \n\n            uni = set()\n            cnt = 0\n            res = g.KruskalMST()\n            for u, v, t in res:\n                if t == 3:\n                    uni.add((u, v))\n                else:\n                    cnt += 1\n\n            g = Graph(n)\n            for t, u, v in edges:\n                if t in [2, 3]:\n                    g.addEdge(u-1, v-1, t)\n\n            res = g.KruskalMST()\n            for u, v, t in res:\n                if t == 3:\n                    uni.add((u, v))\n                else:\n                    cnt += 1\n            return len(edges) - len(uni) - cnt\n        except:\n            return -1\n", "class DisjointSet:\n    def __init__(self, number_of_sites):\n        self.parent = [i for i in range(number_of_sites+1)]\n        self.children_site_count = [1 for _ in range(number_of_sites+1)]\n        self.component_count = number_of_sites\n\n    def find_root(self, site):\n        root = site\n        while root != self.parent[root]:\n            root = self.parent[root]\n        while site != root:\n            site, self.parent[site] = self.parent[site], root\n        return root\n\n    def is_connected(self, site_1, site_2):\n        return self.find_root(site_1) == self.find_root(site_2)\n\n    def union(self, site_1, site_2):\n        site_1_root = self.find_root(site_1)\n        site_2_root = self.find_root(site_2)\n        if site_1_root == site_2_root:\n            return False\n\n        if self.children_site_count[site_1_root] < self.children_site_count[site_2_root]:\n            self.parent[site_1_root] = site_2_root\n            self.children_site_count[site_2_root] += self.children_site_count[\n                site_1_root]\n        else:\n            self.parent[site_2_root] = site_1_root\n            self.children_site_count[site_1_root] += self.children_site_count[\n                site_2_root]\n        self.component_count -= 1\n        return True\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        alice_disjoint_set = DisjointSet(n)\n        bob_disjoint_set = DisjointSet(n)\n\n        TYPE_OF_COMMON_EDGES = 3\n        TYPE_OF_ALICE_EDGES = 1\n        TYPE_OF_BOB_EDGES = 2\n\n        common_edges = filter(lambda edge: edge[0] == TYPE_OF_COMMON_EDGES, edges)\n        alice_edges = filter(lambda edge: edge[0] == TYPE_OF_ALICE_EDGES, edges)\n        bob_edges = filter(lambda edge: edge[0] == TYPE_OF_BOB_EDGES, edges)\n\n        redundant = 0\n        for _, u, v in common_edges:\n            unioned_in_alice = alice_disjoint_set.union(u, v)\n            unioned_in_bob = bob_disjoint_set.union(u, v)\n            if (not unioned_in_alice) and (not unioned_in_bob):\n                redundant += 1\n\n        for _, u, v in bob_edges:\n            if not bob_disjoint_set.union(u,v):\n                redundant += 1\n                \n        for _, u, v in alice_edges:\n            if not alice_disjoint_set.union(u, v):\n                redundant += 1\n        \n        return redundant if alice_disjoint_set.component_count == 1 and bob_disjoint_set.component_count == 1 else -1", "\nclass DSU:\n    \n    def __init__(self, a):\n        self.par = {x:x for x in a}\n    \n    def merge(self, u, v):\n        rootu = self.find(u)\n        rootv = self.find(v)\n        \n        if rootu == rootv:\n            return False\n        \n        self.par[rootu] = rootv\n        return True\n    \n    def find(self, u):\n        if self.par[u] != u:\n            self.par[u] = self.find(self.par[u])\n        return self.par[u]\n    \n    def roots(self):\n        return set(self.find(u) for u in self.par)\n\n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        dsu1 = DSU(range(1,n+1))\n        dsu2 = DSU(range(1,n+1))\n        \n        grouper = defaultdict(list)\n        for t,u,v in edges:\n            grouper[t].append([u,v])\n        \n        both, alice, bob = grouper[3], grouper[1], grouper[2]\n        \n        ret = 0\n        \n        for u,v in both:\n            if not dsu1.merge(u, v):\n                ret += 1\n            dsu2.merge(u, v)\n                \n        for u,v in alice:\n            if not dsu1.merge(u, v):\n                ret += 1\n        \n        for u,v in bob:\n            if not dsu2.merge(u, v):\n                ret += 1\n        \n        if len(dsu1.roots()) != 1 or len(dsu2.roots()) != 1:\n            return -1\n            \n        return ret", "class Solution:\n    def find(self,root:List[int], x:int):\n        if x != root[x]:\n            root[x] = self.find(root,root[x])\n        return root[x]\n        \n    def uni(self,root:List[int], x:int,y:int)->bool:\n        x,y = self.find(root,x), self.find(root,y)\n        if x == y:\n            return False\n        root[x] = y\n        return True\n        \n        \n        \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # feasiblilty check for alice and bob\n\n        # max connected set of type 3\n        l = len(edges)\n        root = list(range(0,n+1))\n        for ed in edges:\n            if ed[0] == 3:\n                self.uni(root,ed[1],ed[2])\n        \n        for i in range(1,n+1):\n            self.find(root,i)\n                    \n        sero = set(root)\n        np = len(sero) - 1\n\n        ret = l-(n+np-2)        \n        if ret < 0:\n            return -1\n\n        \n        root1 = root.copy()\n        root2 = root.copy()\n        for ed in edges:\n            if ed[0] == 1:\n                self.uni(root1,ed[1],ed[2])\n            if ed[0] == 2:\n                self.uni(root2,ed[1],ed[2])\n        \n\n        for i in range(1,n+1):\n            self.find(root1,i)\n            self.find(root2,i)\n\n        if len(set(root1)) == 2 and len(set(root2)) ==2:        \n            return ret\n        else:\n            return -1\n        \n        \n        ret = l - (size-ll + 2*(n-size+ll-1))\n        if ret < 0:\n            return -1\n        else:\n            return ret\n\n        # make connected ones\n        l = len(edges)\n        conn = [0 for x in range(0,n)]\n        nn = [0 for x in range(0,n)]\n        cnt = 2\n        for ed in edges:\n            if ed[0] == 3:\n                nn[ed[1]] = nn[ed[2]] = 2\n        \n        # is fiseable for alice and bob\n        \n        \n        # \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        def find(x):\n            if x != father[x]:\n                # path compression\n                father[x] = find(father[x])\n            return father[x]\n        \n        def union(x, y):\n            fx, fy = find(x), find(y)\n            if fx != fy:\n                father[fy] = fx\n                    \n        father = list(range(n + 1))\n        r = g = 0\n        res = 0\n        for t, u, v in edges:\n            if t == 3:\n                if find(u) != find(v):\n                    union(u, v)\n                    r += 1\n                    g += 1\n                else:\n                    res += 1\n        \n        father0 = father[:]\n        for t, u, v in edges:\n            if t == 1:\n                if find(u) != find(v):\n                    union(u, v)\n                    r += 1\n                else:\n                    res += 1\n        \n        father = father0\n        for t, u, v in edges:\n            if t == 2:\n                if find(u) != find(v):\n                    union(u, v)\n                    g += 1\n                else:\n                    res += 1\n        return res if r == g == n - 1 else -1", "from collections import defaultdict\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # Union find\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n\n        res = e1 = e2 = 0\n\n        # Alice and Bob\n        root = list(range(n + 1))\n        for t, i, j in edges:\n            if t == 3:\n                if uni(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        root0 = root[:]\n\n        # only Alice\n        for t, i, j in edges:\n            if t == 1:\n                if uni(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n\n        # only Bob\n        root = root0\n        for t, i, j in edges:\n            if t == 2:\n                if uni(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if e1 == e2 == n - 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n        \n        res = e1 = e2 = 0\n        \n        # Alice and Bob\n        root = [_ for _ in range(n + 1)]\n        for t, i, j in edges:\n            if t == 3:\n                if uni(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        root0 = root[:]\n\n        # only Alice\n        for t, i, j in edges:\n            if t == 1:\n                if uni(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n        \n        if e1 != n - 1:\n            return -1\n        \n        # only Bob\n        root = root0\n        for t, i, j in edges:\n            if t == 2:\n                if uni(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if e2 == n - 1 else -1\n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        #insight: check if connected, by DFS with both parties\n        #if not, return -1, if so, then a tree may be possible\n        \n        # treat generally connected groups as single nodes\n        # for each person, if DFS possible, then simply subtract unnecessary ones\n        \n#         # union join\n#         count = 1\n        \n#         group = {}\n        \n#         for t, u, v in edges:\n#             if t == 3:  # general links\n#                 if u in group:\n#                     v = group[u]\n#                 elif v in group:\n#                     u = group[v]\n#                 else:  # new node\n#                     group[u] = count\n#                     group[v] = count\n#                     count += 1\n        \n        \n#         print(group)\n        \n#         return 0\n                    \n        \n        # construct both graph\n        from collections import defaultdict\n        gboth = defaultdict(list)\n        \n        edge_counts = defaultdict(int)\n        for t, u, v in edges:\n            edge_counts[t] += 1\n            if t == 3:\n                gboth[u].append(v)\n                gboth[v].append(u)\n                \n        print(gboth)\n        \n        group = {}\n        nodes = set(range(1, n+1))\n        seen = set()\n        count = 1\n        \n        # print(nodes)\n        \n        def dfs(node, gnum):\n            if node not in seen:\n                seen.add(node)\n                if node in nodes:\n                    nodes.remove(node)\n                group[node] = gnum\n                for v in gboth[node]:\n                    dfs(v, gnum)\n        \n        while nodes:\n            dfs(nodes.pop(), count)\n            count += 1\n        count -= 1 # now it reps number of clusters\n        \n        print(group)\n        print(\\\"edge couts\\\", edge_counts)\n        # construct graphs for A & B, see if both possible.\n        \n        graphA = defaultdict(list)\n        graphB = defaultdict(list)\n        graphs = {1: graphA, 2: graphB}\n        \n        for t, u, v in edges:\n            if group[u] != group[v]:\n                if t == 1:\n                    graphA[group[u]].append(group[v])\n                    graphA[group[v]].append(group[u])\n                elif t == 2:\n                    graphB[group[u]].append(group[v])\n                    graphB[group[v]].append(group[u])\n        \n        print(graphA, graphB)\n        \n        def dfs_a(node, person):\n            if node not in seen:\n                seen.add(node)\n                \n                for target in graphs[person][node]:\n                    dfs_a(target, person)\n        \n        for i in range(1, 3):\n            seen.clear()\n            dfs_a(1, i)\n            if len(seen) != count: # not connected\n                print(\\\"disc,\\\", seen, count)\n                return -1\n        else:\n            general_edges = edge_counts[3] - (n - count)\n            a_edges = edge_counts[1] - count + 1\n            b_edges = edge_counts[2] - count + 1\n            return general_edges + a_edges + b_edges", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        amy = UnionFind(n)\n        bob = UnionFind(n)\n        edges.sort(key=lambda x: x[0], reverse=True)\n        added = 0\n        for e in edges:\n            t = e[0]\n            s = e[1]\n            des = e[2]\n            if t == 3:\n                a = amy.union(s-1, des-1)\n                b = bob.union(s-1, des-1)\n                if a or b:\n                    added += 1\n            elif t == 1:\n                if amy.union(s-1, des-1):\n                    added += 1\n            elif t == 2:\n                if bob.union(s-1, des-1):\n                    added += 1\n        if amy.united() and bob.united():\n            return len(edges) - added\n        return -1\n        \n        \nclass UnionFind:\n    def __init__(self, n):\n        self.parent = [i for i in range(n)]\n        self.rank = [0] * n\n        self.distinct = n\n    \n    def find(self, a):\n        if self.parent[a] == a:\n            return a\n        self.parent[a] = self.find(self.parent[a])\n        return self.parent[a]\n        \n    def union(self, a, b):\n        pa = self.find(a)\n        pb = self.find(b)\n        if pa == pb:\n            return False\n        if self.rank[pa] < self.rank[pb]:\n            self.parent[pa] = pb\n            self.rank[pb] += 1\n        else:\n            self.parent[pb] = pa\n            self.rank[pa] += 1\n        self.distinct -= 1\n        return True\n\n    def united(self):\n        return self.distinct == 1\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, e: List[List[int]]) -> int:\n        def union(UF, u, v):            \n            UF[find(UF, v)] = find(UF, u)\n        def find(UF, u):\n            if UF[u] != u: UF[u] = find(UF, UF[u])\n            return UF[u]         \n        def check(UF, t):            \n            UF = UF.copy()\n            for tp, u, v in e:\n                if tp == t: \n                    if find(UF, u) == find(UF, v): self.ans += 1\n                    else: union(UF, u, v)\n            return len(set(find(UF, u) for u in UF)) == 1, UF\n        \n        self.ans, UF = 0, {u: u for u in range(1, n+1)}                \n        UF = check(UF, 3)[1]\n        if not check(UF, 1)[0] or not check(UF, 2)[0]: return -1        \n        return self.ans                        ", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        result = 0\n        s1, s2 = 0, 0\n        uf = UnionFind(n)\n        for t, i, j in edges:\n            if t != 3:\n                continue\n            if uf.union(i, j) is True:\n                result += 1\n            else:\n                s1 += 1\n                s2 += 1\n        parent = list(uf.parent)\n        for t, i, j in edges:\n            if t != 1:\n                continue\n            if uf.union(i, j) is True:\n                result += 1\n            else:\n                s1 += 1\n        uf.parent = parent\n        for t, i, j in edges:\n            if t != 2:\n                continue\n            if uf.union(i, j) is True:\n                result += 1\n            else:\n                s2 += 1\n        return result if s1 == s2 == n-1 else -1\n\n    \nclass UnionFind:\n    def __init__(self, n):\n        self.parent = [i for i in range(n+1)]\n    \n    def find(self, i):\n        if self.parent[i] != i:\n            self.parent[i] = self.find(self.parent[i])\n        return self.parent[i]\n    \n    def union(self, i, j):\n        pi, pj = self.find(i), self.find(j)\n        if pi == pj:\n            return True\n        self.parent[pi] = pj\n        return False", "class Solution:\n    def maxNumEdgesToRemove(self, N: int, edges: List[List[int]]) -> int:\n        def find(x):\n            if x != parent[x]:\n                parent[x] = find(parent[x])\n            return parent[x]\n        \n        def union(x, y):\n            x, y = find(x), find(y)\n            if x == y:\n                return 0\n            parent[x] = y\n            return 1\n        \n        res, e1, e2 = 0, 0, 0\n        parent = [x for x in range(N+1)]\n        # Alice and Bob\n        for t, x, y in edges:\n            if t == 3:\n                if union(x, y):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        parent_ = parent[:]\n        \n        # only Alice\n        for t, x, y in edges:\n            if t == 1:\n                if union(x, y):\n                    e1 += 1\n                else:\n                    res += 1\n                    \n        # only Bob\n        parent = parent_\n        for t, x, y in edges:\n            if t == 2:\n                if union(x, y):\n                    e2 += 1\n                else:\n                    res += 1\n\n        return res if (e1 == N-1 and e2 == N-1) else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(i):\n            if i != parent[i]:\n                parent[i] = find(parent[i])\n            return parent[i]\n        \n        def union(x, y):\n            x, y = find(x), find(y)\n            if x == y:\n                return 0\n            parent[x] = y\n            return 1\n        \n        parent = list(range(n+1))\n        e1 = e2 = res = 0\n        for t, u, v in edges:\n            if t == 3:\n                if union(u, v):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n                    \n        parenttemp = parent[:]\n        \n        for t, u, v in edges:\n            if t == 1:\n                if union(u, v):\n                    e1 += 1\n                else:\n                    res += 1\n        if e1 != n-1:\n            return -1\n        \n        parent = parenttemp\n        \n        for t, u, v in edges:\n            if t == 2:\n                if union(u, v):\n                    e2 += 1\n                else:\n                    res += 1\n        if e2 != n-1:\n            return -1\n        \n        return res", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # Union find\n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n\n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x == y: return 0\n            root[x] = y\n            return 1\n\n        res, e1, e2 = [0], [0], [0]\n\n        t1, t2, t3 = [], [], []\n        for i in range(len(edges)):\n            t = edges[i][0]\n            if t == 1:\n                t1.append(i)\n            elif t == 2:\n                t2.append(i)\n            elif t == 3:\n                t3.append(i)\n\n        # Alice and Bob\n        root = [i for i in range(n + 1)]\n        for k in t3:\n            t, i, j = edges[k]\n            if uni(i, j):\n                e1[0] += 1\n                e2[0] += 1\n            else:\n                res[0] += 1\n        root0 = root[:]\n\n        # only Alice\n        for k in t1:\n            t, i, j = edges[k]\n            if uni(i, j):\n                e1[0] += 1\n            else:\n                res[0] += 1\n\n        # only Bob\n        root = root0\n        for k in t2:\n            t, i, j = edges[k]\n            if uni(i, j):\n                e2[0] += 1\n            else:\n                res[0] += 1\n\n        return res[0] if e1[0] == e2[0] == n - 1 else -1\n            \n            \n", "class UnionFind:\n    def __init__(self, n):\n        self.parents = list(range(n+1))\n        self.ranks = [0] * (n+1)\n        self.size = 1\n    \n    def find(self, x):\n        if self.parents[x] != x:\n            self.parents[x] = self.find(self.parents[x])\n        return self.parents[x]\n    \n    def union(self, x, y):\n        xpar, ypar = self.find(x), self.find(y)\n        if xpar == ypar:\n            # already in same set\n            return False\n        xrank, yrank = self.ranks[x], self.ranks[y]\n        if xrank > yrank:\n            self.parents[ypar] = xpar\n        elif xrank < yrank:\n            self.parents[xpar] = ypar\n        else:\n            self.parents[xpar] = ypar\n            self.ranks[ypar] += 1\n        self.size += 1\n        return True\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1, uf2, ans = UnionFind(n), UnionFind(n), 0\n\\t\\t\n        for t, u, v in edges:\n            if t != 3:\n                continue\n            if not uf1.union(u, v) or not uf2.union(u, v):\n                ans += 1\n        \n        for t, u, v in edges:\n            if t == 1 and not uf1.union(u, v):\n                ans += 1\n            elif t == 2 and not uf2.union(u, v):\n                ans += 1\n   \n        return ans if uf1.size == n and uf2.size == n else -1\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        def find(x):\n            if x != graph[x]:\n                graph[x] = find(graph[x])\n            return graph[x]\n        \n        def union(x, y):\n            px, py = find(x), find(y)\n            if px != py:\n                if size[px] > size[py]:\n                    graph[py] = px\n                else:\n                    graph[px] = py\n                \n                return 1\n            return 0\n        \n        graph = [i for i in range(n + 1)]\n        size = [1] * (n + 1)\n        res = alice = bob = 0\n        for t, i, j in edges:\n            if t == 3:\n                if union(i, j):\n                    #get one more edge\n                    alice += 1\n                    bob += 1\n                else:\n                    # i, j has been connected, this one is not necessary\n                    res += 1\n        \n        tmpG = graph[:]\n        for t, i, j in edges:\n            if t == 1:\n                if union(i, j):\n                    alice += 1\n                else:\n                    res += 1\n        \n        graph = tmpG\n        \n        for t, i, j in edges:\n            if t == 2:\n                if union(i, j):\n                    bob += 1\n                else:\n                    res += 1\n        \n        return res if alice == bob == n - 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        def find(x):\n            if x != graph[x]:\n                graph[x] = find(graph[x])\n            return graph[x]\n        \n        def union(x, y):\n            px, py = find(x), find(y)\n            if px != py:\n                graph[px] = py\n                return 1\n            return 0\n        \n        graph = [i for i in range(n + 1)]\n        res = alice = bob = 0\n        for t, i, j in edges:\n            if t == 3:\n                if union(i, j):\n                    #get one more edge\n                    alice += 1\n                    bob += 1\n                else:\n                    # i, j has been connected, this one is not necessary\n                    res += 1\n        \n        tmpG = graph[:]\n        for t, i, j in edges:\n            if t == 1:\n                if union(i, j):\n                    alice += 1\n                else:\n                    res += 1\n        \n        graph = tmpG\n        \n        for t, i, j in edges:\n            if t == 2:\n                if union(i, j):\n                    bob += 1\n                else:\n                    res += 1\n        \n        return res if alice == bob == n - 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, e: List[List[int]]) -> int:\n        def union(UF, u, v):\n            pu, pv = find(UF, u), find(UF, v)\n            if pu != pv: UF[pv] = pu\n        def find(UF, u):\n            if UF[u] != u: UF[u] = find(UF, UF[u])\n            return UF[u]         \n        def check(UF, t):            \n            UF = UF.copy()\n            for tp, u, v in e:\n                if tp != t: continue\n                pu, pv = find(UF, u), find(UF, v)\n                if pu == pv: self.ans += 1\n                else: union(UF, u, v)\n            return len(set(find(UF, u) for u in UF)) == 1, UF\n        \n        self.ans, UF = 0, {u: u for u in range(1, n+1)}                \n        UF = check(UF, 3)[1]\n        if not check(UF, 1)[0] or not check(UF, 2)[0]: return -1        \n        return self.ans                        ", "class Solution:\n    def _root(self, U, a):\n        while U[a] != a:\n            U[a] = U[U[a]]\n            a = U[a]\n        return a\n    \n    def _union(self, U, a, b):\n        ra = self._root(U, a)\n        rb = self._root(U, b)\n        if ra == rb:\n            return False\n        U[ra] = rb\n        return True\n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges.sort(key=lambda x: x[0], reverse=True)\n        \n        ua = [i for i in range(n + 1)]\n        ub = [i for i in range(n + 1)]\n        e1, e2, e3 = 0, 0, 0\n        \n        for ty, u, v in edges:\n            if ty == 3:\n                tmp = self._union(ua, u, v)\n                tmp = self._union(ub, u, v) or tmp\n                if tmp:\n                    e3 += 1\n            elif ty == 2:\n                if self._union(ub, u, v):\n                    e2 += 1\n            elif ty == 1:\n                if self._union(ua, u, v):\n                    e1 += 1\n        \n        ca, cb = 0, 0\n        for i in range(1, n + 1):\n            if ua[i] == i:\n                ca += 1\n            if ub[i] == i:\n                cb += 1\n            if ca > 1 or cb > 1:\n                return -1\n                \n        return len(edges) - e1 - e2 - e3", "class UnionFind:\n    def __init__(self, n):\n        self.parentArr = [i for i in range(1 + n)]\n        self.groupSize = [1 for i in range(1 + n)]\n        self.numGroups = n\n    \n    def union(self, i, j):\n        root_i, root_j = self.getRoot(i), self.getRoot(j)\n        \n        self.numGroups -= 1\n        \n        if self.groupSize[root_i] < self.groupSize[root_j]:\n            self.parentArr[root_i] = root_j\n            self.groupSize[root_j] += self.groupSize[root_i]\n        else:\n            self.parentArr[root_j] = root_i\n            self.groupSize[root_i] += self.groupSize[root_j]\n        \n    \n    def getRoot(self, i):\n        if self.parentArr[i] == i:\n            return i\n        else:\n            ans = self.getRoot(self.parentArr[i])\n            self.parentArr[i] = ans\n            return ans\n        \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        edges.sort()\n        \n        uf_alice = UnionFind(n)\n        uf_bob = UnionFind(n)\n        \n        ans = 0\n        for edge in reversed(edges):\n            edgeType, u, v = edge\n            \n            if edgeType == 3:\n                root_u, root_v = uf_alice.getRoot(u), uf_alice.getRoot(v)\n                \n                if (root_u != root_v):\n                    uf_alice.union(u, v)\n                    uf_bob.union(u, v)\n                    ans += 1\n            elif edgeType == 1:\n                root_u, root_v = uf_alice.getRoot(u), uf_alice.getRoot(v)\n                if root_u != root_v:\n                    uf_alice.union(u, v)\n                    ans += 1\n            else:\n                root_u, root_v = uf_bob.getRoot(u), uf_bob.getRoot(v)\n                \n                if root_u != root_v:\n                    uf_bob.union(u, v)\n                    ans += 1\n            \n            if uf_alice.numGroups == 1 and uf_bob.numGroups == 1:\n                break\n        \n        return len(edges) - ans if (uf_alice.numGroups == 1 and uf_bob.numGroups == 1) else -1\n        \n                    \n                \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(p, i):\n            j = i\n            while j != p[j]:\n                p[j] = p[p[j]]\n                j = p[j]\n            \n            p[i] = j\n            return j\n        \n        def join(p, i, j):\n            pi = find(p, i)\n            pj = find(p, j)\n            p[pi] = pj\n            \n        \n        e = collections.defaultdict(list)\n        for t, u, v in edges:\n            e[t].append((u, v))\n        \n        \n        \n        def build_mst(p, e):\n            remove = 0\n            for u, v in e:\n                pu, pv = find(p, u), find(p, v)\n                if pu == pv:\n                    remove += 1\n                else:\n                    join(p, u, v)\n            return remove\n        \n        p = list(range(n + 1))\n        remove = build_mst(p, e[3])\n        print(p, remove)\n        \n        p_alice, p_bob = p[::], p[::]\n        remove_alice = build_mst(p_alice, e[1])\n        remove_bob = build_mst(p_bob, e[2])\n        if len(set([find(p_alice, i + 1) for i in range(n)])) > 1:\n            return -1\n        \n        \n        if len(set([find(p_bob, i + 1) for i in range(n)])) > 1:\n            return -1\n        \n        return remove + remove_alice + remove_bob", "import copy\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(u):\n            if root[u] != u:\n                root[u] = find(root[u])\n            return root[u]\n        def union(u, v):\n            ru, rv = find(u), find(v)\n            if ru == rv:\n                return 0\n            root[ru] = root[rv]\n            return 1\n        \n        root = [i for i in range(n+1)]\n        res = e1 = e2 = 0\n        for t, i, j in edges:\n            if t == 3:\n                if union(i, j):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        rootCopy = copy.deepcopy(root)\n        for t, i, j in edges:\n            if t == 1:\n                if union(i, j):\n                    e1 += 1\n                else:\n                    res += 1\n        \n        root = rootCopy\n        for t, i, j in edges:\n            if t == 2:\n                if union(i, j):\n                    e2 += 1\n                else:\n                    res += 1\n        return res if e1 == n-1 and e2 == n-1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        '''\n        all of them are connected so the only possibility that is invalid is that its only type 1 or only type2 -> -1\n        the rest is 0 - \n        find cycles ?\n        find if we have type3, we dont need type1 or type2 -> check if we have enough type 3 \n        then we check type 1 and 2 \n        UNION FIND !!!\n        '''\n        root = [k for k in range(n+1)]\n       \n        def find(i):\n            if i != root[i]:\n                root[i] = find(root[i])\n            return root[i]\n        \n        def union(val1,val2):\n            val1,val2 = find(val1),find(val2)\n            if val1 == val2:\n                return 1\n            root[val1] = val2\n            return 0\n        \n        remove = t1 = t2 = 0\n        for ty,fr,to in edges:\n            if ty == 3:\n                if union(fr,to):\n                    remove += 1\n                else:\n                    t1 +=1\n                    t2 +=1\n\n        temp_root = root[:]\n        for ty,fr,to in edges:\n            if ty == 1:\n                if union(fr,to):\n                    remove += 1\n                else:\n                    t1 += 1\n                        \n        root = temp_root\n        for ty,fr,to in edges:       \n            if ty == 2:\n                if union(fr,to):\n                    remove += 1\n                else:\n                    t2 += 1\n               \n        return remove if (t1 == t2 == n-1) else -1\n", "from copy import deepcopy\n\nclass DSU:\n    def __init__(self, n):\n        self.dsu = [i for i in range(n+1)]\n        \n    def find(self, x):\n        if x == self.dsu[x]:\n            return x\n        self.dsu[x] = self.find(self.dsu[x])\n        return self.dsu[x]\n    \n    def union(self, x, y):\n        xr = self.find(x)\n        yr = self.find(y)\n        self.dsu[yr] = xr\n        return\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        alice = []\n        bob = []\n        both = []\n        for t, x, y in edges:\n            if t == 1:\n                alice.append((x, y))\n            elif t == 2:\n                bob.append((x, y))\n            else:\n                both.append((x, y))\n        dsu = DSU(n)\n        counter3 = 0\n        for x, y in both:\n            if dsu.find(x) == dsu.find(y):\n                continue\n            dsu.union(x, y)\n            counter3 += 1\n        dsu1 = deepcopy(dsu)\n        counter1 = 0\n        for x, y in alice:\n            if dsu1.find(x) == dsu1.find(y):\n                continue\n            dsu1.union(x, y)\n            counter1 += 1\n        # print(dsu1.dsu)\n        dsu2 = deepcopy(dsu)\n        counter2 = 0\n        for x, y in bob:\n            if dsu2.find(x) == dsu2.find(y):\n                continue\n            dsu2.union(x, y)\n            counter2 += 1\n        # print(dsu2.dsu)\n        if counter1 + counter3 != n-1 or counter2 + counter3 != n-1:\n            return -1\n        else:\n            return len(edges) + counter3 - 2*n +2\n", "class Solution:\n    def find(self, v):\n        if self.vertices[v] != v:\n            self.vertices[v] = self.find(self.vertices[v])\n        return self.vertices[v]\n    \n    def union(self, u, v):\n        up, vp = self.find(u), self.find(v)\n        if up == vp:\n            return False\n        self.vertices[up] = vp\n        return True\n       \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        self.vertices = list(range(n + 1))\n        e1, e2, ret = 0, 0, 0\n        for t, u, v in edges:\n            if t != 3:\n                continue\n            if self.union(u, v):\n                e1 += 1\n                e2 += 1\n            else:\n                ret += 1\n        self.vertices_sved = self.vertices[::]\n        for t, u, v in edges:\n            if t != 1:\n                continue\n            if self.union(u, v):\n                e1 += 1\n            else:\n                ret += 1              \n        if e1 != n - 1:\n            return -1\n        self.vertices = self.vertices_sved\n        for t, u, v in edges:\n            if t != 2:\n                continue\n            if self.union(u, v):\n                e2 += 1\n            else:\n                ret += 1\n        if e2 != n - 1:\n            return -1                \n        return ret", "class Solution:\n    def find(self, i:int, nodes_ptrs: List[int]):\n        \n        ptr = i\n        \n        ptr_prev = []\n        \n        while nodes_ptrs[ptr] != ptr:\n            ptr_prev.append(ptr)\n            ptr = nodes_ptrs[ptr]\n        \n        for pr in ptr_prev:\n            nodes_ptrs[pr] = ptr\n        \n        return ptr\n    \n#     def union(self, i:int, j:int, nodes_ptrs:List[int]):\n        \n#         ptr_i = find(i, node_ptrs)\n#         ptr_j = find(j, node_ptrs)\n        \n#         if(ptr_i == ptr_j): return 0\n#         else:\n#             nodes_ptrs[ptr_i] = ptr_j\n        \n#         return ptr_j\n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        ANodes = list(range(n+1))\n        BNodes = list(range(n+1))\n        \n        AConnected = {}\n        BConnected = {}\n        \n        AMaxConnect = 1\n        BMaxConnect = 1\n        \n        n_used = 0\n        \n        edges_traverse = [0]*len(edges)\n        \n        j = 0\n        k = -1\n        for i in range(len(edges)):\n            if (edges[i][0] == 3):\n                edges_traverse[j] = i\n                j += 1\n            else:\n                edges_traverse[k] = i\n                k -= 1\n        \n        for i in range(len(edges_traverse)):\n            \n            [typei, u, v] = edges[edges_traverse[i]]\n            #print(type_i, u_i, vi)\n            \n            include_A = False\n            include_B = False\n            \n            #Exam Alice\n            \n            u_ptr_A = self.find(u, ANodes)\n            v_ptr_A = self.find(v, ANodes)\n            u_ptr_B = self.find(u, BNodes)\n            v_ptr_B = self.find(v, BNodes)\n            \n            \n            if typei != 2 and u_ptr_A != v_ptr_A:\n                include_A = True\n            \n            #Exam Bob\n            if typei != 1 and u_ptr_B != v_ptr_B:\n                include_B = True\n            \n            include = include_A or include_B\n            \n            # print(include, n_used)\n            \n            if (include):\n                \n                n_used += 1\n                \n                if(include_A):\n                    num_ui_set = AConnected.get(u_ptr_A, 1) \n                    num_vi_set = AConnected.get(v_ptr_A, 1)\n                \n\n                    ANodes[u_ptr_A] = v_ptr_A\n                    AConnected[v_ptr_A] = num_ui_set + num_vi_set\n                    if AConnected[v_ptr_A] > AMaxConnect:\n                        AMaxConnect = AConnected[v_ptr_A]\n                \n                if(include_B):\n                    num_ui_set = BConnected.get(u_ptr_B, 1) \n                    num_vi_set = BConnected.get(v_ptr_B, 1)\n                \n                    BNodes[u_ptr_B] = v_ptr_B\n                    BConnected[v_ptr_B] = num_ui_set + num_vi_set\n                    if BConnected[v_ptr_B] > BMaxConnect:\n                        BMaxConnect = BConnected[v_ptr_B]\n                \n                if(AMaxConnect == n and BMaxConnect == n): break\n            \n            # print(BNodes)\n            # print(ANodes)\n        \n        if(AMaxConnect != n or BMaxConnect !=n): return -1\n        \n        return len(edges)-n_used\n            \n            \n", "import collections\nclass Solution:\n    def find(self, i): \n        if self.root[i]==i: return self.root[i]\n        self.root[i]=self.find(self.root[i])\n        return self.root[i]\n    \n    def union(self, i, j):\n        ri=self.find(i)\n        rj=self.find(j)\n        self.root[rj]=ri\n        return \n    \n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n            dA=collections.defaultdict(list)\n            dB=collections.defaultdict(list)\n            d=collections.defaultdict(list)\n            A=0\n            B=0\n            res=0\n            for t,u,v in edges:\n                if t==1: \n                    dA[u-1].append(v-1)\n                elif t==2: \n                    dB[u-1].append(v-1)\n                else: d[u-1].append(v-1)\n            self.root=[i for i in range(n)]\n            \n            for u in d:\n                for v in d[u]:\n                    if self.find(u)==self.find(v): res+=1\n                    else: self.union(u,v)\n            \n            \n            temp=self.root.copy()\n            for u in dA:\n                for v in dA[u]:\n                    if self.find(u)==self.find(v):\n                        res+=1\n                    else:\n                        self.union(u,v)\n            if len(set([self.find(i) for i in range(n)]))>1: return -1\n            self.root=temp\n            \n            for u in dB:\n                for v in dB[u]:\n                    if self.find(u)==self.find(v):\n                        res+=1\n                    else:\n                        self.union(u,v)\n            if len(set([self.find(i) for i in range(n)]))>1: return -1\n            return res\n            \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n\n        ufa = UnionFind(n) # Graph for Alice\n        ufb = UnionFind(n) # Graph for Bob\n        cnt = 0 # number of removable edges\n        \n        for x, y, z in edges:\n            if x == 3:\n                flag1 = ufa.union(y, z)\n                flag2 = ufb.union(y, z)\n                if not flag1 or not flag2: cnt +=1\n\n        for x, y, z in edges:\n            if x == 1:\n                flag = ufa.union(y, z)\n                if not flag: cnt += 1\n            if x == 2:\n                flag = ufb.union(y, z)\n                if not flag: cnt += 1\n\n        return cnt if ufa.groups == 1 and ufb.groups == 1 else -1\n            \n        \nclass UnionFind():\n    def __init__(self, n):\n        self.parents = {i:i for i in range(1, n+1)}\n        self.groups = n\n\n    def find(self, x):\n        if self.parents[x] == x:\n            return x\n        else:\n            self.parents[x] = self.find(self.parents[x])\n            return self.parents[x]\n\n    def union(self, x, y):\n        x = self.find(x)\n        y = self.find(y)\n\n        if x == y:\n            return False\n\n        self.parents[y] = x\n        self.groups -= 1\n        return True\n", "class UnionFind:\n    def __init__(self, n):\n        self.root = list(range(n + 1))\n    \n    def find(self, i):\n        if self.root[i] != i:\n            self.root[i] = self.find(self.root[i])\n        return self.root[i]\n    \n    def union(self, x, y):\n        rx, ry = self.find(x), self.find(y)\n        if rx == ry:\n            return True\n        self.root[rx] = ry\n        return False\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        if n < 1:\n            return 0\n        if len(edges) < n - 1:\n            return -1\n        uf = UnionFind(n)\n        ans = e1 = e2 = 0\n        for t, u, v in edges:\n            if t == 3:\n                if uf.union(u, v):\n                    ans += 1\n                else:\n                    e1 += 1\n                    e2 += 1\n        root_copy = uf.root[:]\n        for t, u, v in edges:\n            if t == 1:\n                if uf.union(u, v):\n                    ans += 1\n                else:\n                    e1 += 1\n        uf.root = root_copy\n        for t, u, v in edges:\n            if t == 2:\n                if uf.union(u, v):\n                    ans += 1\n                else:\n                    e2 += 1\n        return ans if e1 == e2 == n - 1 else -1", "import copy\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        parent = [0]*n\n        ans = 0\n        alice = 0\n        bob = 0\n\n        def find(x):\n            if parent[x] != x:\n                parent[x] = find(parent[x])\n            return parent[x]\n        def union(x, y):\n            px, py = find(x), find(y)\n            if px == py:\n                return False\n            parent[py] = px\n            return True\n        \n        #both\n        for i in range(n):\n            parent[i] = i\n            \n        for i in range(len(edges)):\n            t, u, v = edges[i]\n            if t == 3:\n                if union(u-1,v-1):\n                    alice += 1\n                    bob += 1\n                else:\n                    ans += 1\n                    \n        #alice\n        ogparent = copy.deepcopy(parent)\n        for i in range(len(edges)):\n            t, u, v = edges[i]\n            if t == 1:\n                if union(u-1,v-1):\n                    alice += 1\n                else:\n                    ans += 1\n        for i in range(n):\n            parent[i] = i\n        #bob\n        parent = copy.deepcopy(ogparent)\n        for i in range(len(edges)):\n            t, u, v = edges[i]\n            if t == 2:\n                if union(u-1, v-1):\n                    bob += 1\n                else:\n                    ans += 1\n        print((alice, bob, ans))\n        if alice == n-1 and bob == n-1:\n            return ans\n        return -1\n                \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(p, u):\n            parent = p[u]\n            if parent == u:\n                return parent\n            \n            p[u] = find(p, parent)\n            return p[u]\n        \n        def union(p, rank, root_u, root_v):\n            if rank[root_u] < rank[root_v]:\n                p[root_u] = root_v\n            elif rank[root_v] < rank[root_u]:\n                p[root_v] = root_u\n            else:\n                p[root_u] = root_v\n                rank[root_v] += 1\n        \n        p = list(range(n))\n        rank = [0] * n\n        \n        full_edges = set()\n        partial_edges = set()\n        partial_adj = {}\n        partial_adj[1] = collections.defaultdict(set)\n        partial_adj[2] = collections.defaultdict(set)\n        for e in edges:\n            edge_type, u, v = e[0], e[1] - 1, e[2] - 1\n            if edge_type == 3:\n                full_edges.add((u, v))\n            else:\n                partial_edges.add((edge_type, u, v))\n                partial_adj[edge_type][u].add(v)\n                partial_adj[edge_type][v].add(u)\n\n        nb_edges_in_mst = 0\n        for e in full_edges:\n            u, v = e\n            root_u, root_v = find(p, u), find(p, v)\n            if root_u != root_v:\n                union(p, rank, root_u, root_v)\n                nb_edges_in_mst += 1\n        \n        for e in partial_edges:\n            edge_type, v0, v1 = e\n            if find(p, v0) == find(p, v1):\n                continue\n\n            # We have two nodes u and v, u in a fully-connected component A, v in another\n            # fully-connected component B. A disjoint from B and u is partially connected\n            # to v via an edge of `edge_type`. Since we need to reach v from u by both\n            # Alice and Bob, if we can find another node, x, in A that is partially-connected \n            # to v by an edge of `needed_edge_type`, then we have edges of both types that\n            # we can use to reach v from u. (use `edge_type` from u->v, or use `needed_edge_type`\n            # from u->x->v). We can also try the same exercise with u and v swapped.\n            needed_edge_type = 2 if edge_type == 1 else 2\n            for pair in [(v0, v1), (v1, v0)]:\n                u, v = pair\n                found_needed_edge = False\n                for x in partial_adj[needed_edge_type][v]:\n                    root_x = find(p, x)\n                    root_u = find(p, u)\n                    if root_x == root_u:\n                        # x is in in subgraph A, same as u, AND it's connected to v via the\n                        # needed_edge_type\n                        root_v = find(p, v)\n                        union(p, rank, root_x, root_v)\n                        union(p, rank, root_u, root_v)\n                        nb_edges_in_mst += 2\n                        found_needed_edge = True\n                        break\n                if found_needed_edge:\n                    break\n\n        uniq_roots = set()\n        for u in range(len(p)):\n            uniq_roots.add(find(p, u))\n        if len(uniq_roots) != 1:\n            return -1  \n        \n        return len(edges) - nb_edges_in_mst\n            \n                \n            \n        \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def union(node1, node2):\n            root1, root2 = find(node1), find(node2)\n            if root1 != root2:\n                if rank[root1] <= rank[root2]:\n                    parent[root1] = root2\n                    rank[root2] += 1 \n                else:\n                    parent[root2] = root1\n                    rank[root1] += 1\n        \n        def find(node):\n            if parent[node] != node:\n                parent[node] = find(parent[node])\n            return parent[node]\n        \n        parent, rank = {i:i for i in range(1, n + 1)}, {i:0 for i in range(1, n + 1)}\n        ans, n1, n2 = 0, 0, 0\n        for t, node1, node2 in edges:\n            if t == 3:\n                if find(node1) != find(node2):\n                    union(node1, node2)\n                    n1 += 1\n                    n2 += 1\n                else:\n                    ans += 1\n        \n        p = parent.copy()\n        for t, node1, node2 in edges:\n            if t == 1:\n                if find(node1) != find(node2):\n                    union(node1, node2)\n                    n1 += 1\n                else:\n                    ans += 1              \n        \n        parent = p\n        for t, node1, node2 in edges:\n            if t == 2:\n                if find(node1) != find(node2):\n                    union(node1, node2)\n                    n2 += 1\n                else:\n                    ans += 1\n        \n        return ans if n1 == n2 == n - 1 else -1\n", "class UnionFind:\n    def __init__(self, n):\n        self.parents = [i for i in range(n)]\n        self.rank = [0]*n\n        self.count = 1\n    def find(self, x):\n        if x != self.parents[x]:\n            # path compression, recursively\n            self.parents[x] = self.find(self.parents[x])\n        return self.parents[x]\n\n    def union(self, x, y):\n        # find root parents\n        px, py = self.find(x), self.find(y)\n        if px == py:\n            return False\n        if self.rank[px] > self.rank[py]:\n            self.parents[py] = px\n        elif self.rank[px] < self.rank[py]:\n            self.parents[px] = py\n        else:\n            # \u5982\u679c\u76f8\u7b49\uff0c\u52a0rank\n            self.parents[px] = py\n            self.rank[py] += 1\n        self.count += 1\n        return True\n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # undirected map -- redundant detection --> union find  \n        uf1, uf2, ans = UnionFind(n), UnionFind(n), 0\n\\t\\t\n        for t, u, v in edges:\n            if t != 3:\n                continue\n            if not uf1.union(u - 1, v - 1) or not uf2.union(u - 1, v - 1):\n                ans += 1\n        \n        for t, u, v in edges:\n            if t == 1 and not uf1.union(u - 1, v - 1):\n                ans += 1\n            elif t == 2 and not uf2.union(u - 1, v - 1):\n                ans += 1\n   \n        return ans if uf1.count == n and uf2.count == n else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # build graph, use type 3 first\n        # then do alice and bob separately\n        # have dsu parents to build up alice and bob\n        \n        parentsA = list(range(n))\n        parentsB = list(range(n))\n        \n        def find(parents, a):\n            while a != parents[a]:\n                parents[a] = parents[parents[a]]\n                a = parents[a]\n            return a\n            \n        def union(parents, a, b):\n            a = find(parents, a)\n            b = find(parents, b)\n            parents[a] = b\n        \n        type3 = []\n        typeB = []\n        typeA = []\n        \n        for t, u, v in edges:\n            u, v = u-1, v-1 # make zero indexed, easier for UF\n            if t == 3:\n                type3.append((u, v))\n            elif t == 2:\n                typeB.append((u, v))\n            elif t == 1:\n                typeA.append((u, v))\n                \n        # now add type3 edges if they join together two new things\n        \n        tree1, tree2, res = 0,0,0\n        for u, v in type3:\n            if find(parentsA, u) != find(parentsA, v):\n                tree1 += 1\n                tree2 += 1\n                union(parentsA, u, v)\n                union(parentsB, u, v)\n            else:\n                res += 1\n                \n        # now do type1 and 2 separately\n        for u,v in typeA:\n            if find(parentsA, u) != find(parentsA, v):\n                tree1 += 1\n                union(parentsA, u, v)\n            else:\n                res += 1\n        \n        for u,v in typeB:\n            if find(parentsB, u) != find(parentsB, v):\n                tree2 += 1\n                union(parentsB, u, v)\n            else:\n                res += 1\n        \n        if tree1 == n-1 and tree2 == n-1:\n            return res\n        else:\n            return -1", "class DisjointSet():\n    def __init__(self, n):\n        self.parent = [0] * n\n        self.rank = [0] * n\n        for i in range(0, n):\n            self.parent[i] = i\n        \n    def find(self, i):\n        if self.parent[i] != i:\n            self.parent[i] = self.find(self.parent[i])\n        return self.parent[i]\n    \n    def union(self, x, y):\n        x_parent = self.find(x)\n        y_parent = self.find(y)\n        \n        if x_parent == y_parent:\n            return 0\n        else:\n            if self.rank[x_parent] > self.rank[y_parent]:\n                self.parent[y_parent] = x_parent\n            elif self.rank[y_parent] > self.rank[x_parent]:\n                self.parent[x_parent] = y_parent\n            else:\n                self.parent[y_parent] = x_parent\n                self.rank[x_parent] += 1\n            return 1\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        res = e1 = e2 = 0\n        ds = DisjointSet(n)\n        print((ds.parent))\n        for t, u, v in edges:\n            if t == 3:\n                if ds.union(u-1,v-1):\n                    e1 += 1\n                    e2 += 1\n                else:\n                    res += 1\n        print((ds.parent))\n        \n        tmp = copy.deepcopy(ds)\n        for t, u, v in edges:\n            if t == 1:\n                if ds.union(u-1, v-1):\n                    e1 += 1\n                else:\n                    res += 1\n                    \n        \n        \n        for t, u, v in edges:\n            if t == 2:\n                print()\n                if tmp.union(u-1, v-1):\n                    e2 += 1\n                else:\n                    res += 1\n        \n        return res if e1 == e2 == n - 1 else -1\n                    \n        \n        \n", "from copy import deepcopy\nclass UnionFind:\n    def __init__(self, n):\n        self.leaders = [i for i in range(n)]\n        self.ranks = [1 for i in range(n)]\n    \n    def find(self, x):\n        # p = x\n        # while p != self._leaders[p]:\n        #     p = self._leaders[p]\n        # while x != p:\n        #     self._leaders[x], x = p, self._leaders[x]\n        # return p\n        if self.leaders[x] != x:\n            self.leaders[x] = self.find(self.leaders[x])\n        return self.leaders[x]\n    \n    def union(self, x, y):\n        p = self.find(x)\n        q = self.find(y)\n        if p == q: \n            return False\n        if self.ranks[p] < self.ranks[q]:\n            self.leaders[p] = q\n        elif self.ranks[p] > self.ranks[q]:\n            self.leaders[q] = p\n        else:        \n            self.leaders[q] = p\n            self.ranks[p] += 1\n        return True\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n, edges):\n        res, cnt1 = 0, 0\n        uf1 = UnionFind(n + 1)\n        for g, u, v in edges:\n            if g == 3:   \n                if uf1.union(u, v):\n                    cnt1 += 1\n                else:\n                    res += 1\n        \n        uf2 = deepcopy(uf1)\n        cnt2 = cnt1\n        for g, u, v in edges:\n            if g == 1:   \n                if uf1.union(u, v):\n                    cnt1 += 1\n                else:\n                    res += 1\n\n        for g, u, v in edges:\n            if g == 2:   \n                if uf2.union(u, v):\n                    cnt2 += 1\n                else:\n                    res += 1\n        \n        if cnt1 != n - 1 or cnt2 != n - 1:\n            return -1\n        return res", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(x):\n            if x != dsu[x]:\n                dsu[x] = find(dsu[x])\n            return dsu[x]\n\n        def union(x, y):\n            x, y = find(x), find(y)\n            if x == y:\n                return False\n            dsu[x] = y\n            return True\n\n        res = type_1 = type_2 = 0\n        dsu, type_edges = list(range(n + 1)), [[], [], [], []]\n        for t, u, v in edges:\n            type_edges[t].append([u, v])\n        for u, v in type_edges[3]:\n            if union(u, v):\n                type_1 += 1\n                type_2 += 1\n            else:\n                res += 1\n        dsu_bak = dsu[:]\n        for u, v in type_edges[1]:\n            if union(u, v):\n                type_1 += 1\n            else:\n                res += 1\n        dsu = dsu_bak\n        for u, v in type_edges[2]:\n            if union(u, v):\n                type_2 += 1\n            else:\n                res += 1\n        return res if type_1 == type_2 == n - 1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, e: List[List[int]]) -> int:\n        def union(UF, u, v):\n            pu, pv = find(UF, u), find(UF, v)\n            if pu != pv: UF[pv] = pu\n        def find(UF, u):\n            UF.setdefault(u, u)            \n            if UF[u] != u: UF[u] = find(UF, UF[u])\n            return UF[u]        \n        def par_size(UF):\n            return len(set(find(UF, u) for u in range(1, n+1)))        \n        def check(UF, t):            \n            UF = UF.copy()\n            for tp, u, v in e:\n                if tp != t: continue\n                pu, pv = find(UF, u), find(UF, v)\n                if pu == pv: self.ans += 1\n                else: union(UF, u, v)\n            return par_size(UF) == 1\n        \n        self.ans, UF = 0, {}        \n        for t, u, v in e:\n            if t != 3: continue\n            pu, pv = find(UF, u), find(UF, v)\n            if pu == pv: self.ans += 1\n            else: union(UF, u, v)        \n        if not check(UF, 1): return -1\n        if not check(UF, 2): return -1        \n        return self.ans                        ", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, e: List[List[int]]) -> int:\n        def union(UF, u, v):\n            # UF.setdefault(u, u); UF.setdefault(v, v)\n            pu, pv = find(UF, u), find(UF, v)\n            if pu != pv: UF[pv] = pu\n        def find(UF, u):\n            UF.setdefault(u, u)            \n            if UF[u] != u: UF[u] = find(UF, UF[u])\n            return UF[u]\n        \n        def par_size(UF):\n            return len(set(find(UF, u) for u in range(1, n+1)))\n        \n        def check(UF, t):            \n            UF = UF.copy()\n            for tp, u, v in e:\n                if tp != t: continue\n                pu, pv = find(UF, u), find(UF, v)\n                if pu == pv: self.ans += 1\n                else: union(UF, u, v)\n            return par_size(UF) == 1\n        \n        self.ans, UF = 0, {}        \n        for t, u, v in e:\n            if t != 3: continue\n            pu, pv = find(UF, u), find(UF, v)\n            if pu == pv: self.ans += 1\n            else: union(UF, u, v)\n        \n        if not check(UF, 1): return -1\n        if not check(UF, 2): return -1        \n        return self.ans                        ", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges = sorted(edges, key=lambda l:l[0], reverse=True)\n        uf_a = [i for i in range(n)]\n        uf_b = [j for j in range(n)]\n        \n        cnt = 0        \n        for edge in edges:\n            if edge[0] == 3:\n                cnt += self.union(uf_a, edge[1]-1, edge[2]-1)\n                self.union(uf_b, edge[1]-1, edge[2]-1)\n            elif edge[0] == 1:\n                cnt += self.union(uf_a, edge[1]-1, edge[2]-1)\n            else: # edge[0] == 2\n                cnt += self.union(uf_b, edge[1]-1, edge[2]-1)\n        if not self.connected(uf_a) or not self.connected(uf_b):\n            return -1\n        return len(edges)-cnt\n    \n    def connected(self, uf: List[int]) -> bool:\n        r = self.root(uf, 0)\n        for i in range(1, len(uf)):\n            if self.root(uf, i) != r:\n                return False\n        return True\n                \n    def root(self, uf: List[int], a: int) -> int:\n        cur = a\n        while uf[cur] != cur:\n            cur = uf[cur]\n        root = cur\n        while uf[a] != root:\n            parent = uf[a]\n            uf[a] = root\n            a = parent\n        return root\n    \n    def union(self, uf: List[int], a: int, b: int) -> int:\n        root_a = self.root(uf, a)\n        root_b = self.root(uf, b)\n        if root_a == root_b:\n            return 0\n        small = min(root_a, root_b)\n        large = max(root_a, root_b)\n        uf[large] = small\n        return 1\n", "class DSU:\n  def __init__(self, n):\n    self.p = [-1]*(n+1)\n    self.r = [0]*(n+1)\n    \n  def find_parent(self, x):\n    if self.p[x]==-1:\n      return x\n    self.p[x] = self.find_parent(self.p[x]) # path compression\n    return self.p[x]\n  \n  def union(self, a, b):\n    pa = self.find_parent(a)\n    pb = self.find_parent(b)\n    if pa==pb: return False\n    if self.r[pa]>self.r[pb]:\n      self.p[pb] = pa     # here rank can be adding\n    elif self.r[pa]<self.r[pb]:\n      self.p[pa] = pb\n    else:\n      self.p[pa] = pb\n      self.r[pb] += 1\n      \n    return True\n  \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n      edges = sorted(edges, key=lambda x: -x[0])\n      dsu_alice = DSU(n)    # this can be done using on dsu and counting edges for alice and bob. if connected at the end, both tree should have n-1 edges\n      dsu_bob = DSU(n)\n      res = 0\n      \n      for e in edges:\n        if e[0]==3:\n          au = dsu_alice.union(e[1],e[2])\n          bu = dsu_bob.union(e[1],e[2])\n          if not au and not bu:\n            res += 1\n        elif e[0]==1:\n          if not dsu_alice.union(e[1],e[2]):\n            res += 1\n        else:\n          if not dsu_bob.union(e[1],e[2]):\n            res += 1\n        # print (e, res) \n      \n      ap = 0\n      bp = 0\n      for i in range(1, n+1):\n        if ap and dsu_alice.find_parent(i)!=ap:\n          return -1\n        else: ap = dsu_alice.find_parent(i)\n        if bp and dsu_bob.find_parent(i)!=bp:\n          return -1\n        else: bp = dsu_bob.find_parent(i)\n      return res\n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, e: List[List[int]]) -> int:\n        def union(UF, u, v):\n            UF.setdefault(u, u); UF.setdefault(v, v)\n            pu, pv = find(UF, u), find(UF, v)\n            if pu != pv: UF[pv] = pu\n        def find(UF, u):\n            UF.setdefault(u, u)            \n            if UF[u] != u: UF[u] = find(UF, UF[u])\n            return UF[u]\n        \n        def par_size(UF):\n            return len(set(find(UF, u) for u in range(1, n+1)))\n        \n        def check(UF, t):            \n            UF = UF.copy()\n            for tp, u, v in e:\n                if tp != t: continue\n                pu, pv = find(UF, u), find(UF, v)\n                if pu == pv: self.ans += 1\n                else: union(UF, u, v)\n            return par_size(UF) == 1\n        \n        self.ans, UF = 0, {}        \n        for t, u, v in e:\n            if t != 3: continue\n            pu, pv = find(UF, u), find(UF, v)\n            if pu == pv: self.ans += 1\n            else: union(UF, u, v)\n        \n        if not check(UF, 1): return -1\n        if not check(UF, 2): return -1        \n        return self.ans                        ", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        def find(p, u):\n            parent = p[u]\n            if parent == u:\n                return parent\n            \n            p[u] = find(p, parent)\n            return p[u]\n        \n        def union(p, rank, root_u, root_v):\n            if rank[root_u] < rank[root_v]:\n                p[root_u] = root_v\n            elif rank[root_v] < rank[root_u]:\n                p[root_v] = root_u\n            else:\n                p[root_u] = root_v\n                rank[root_v] += 1\n        \n        p = list(range(n))\n        rank = [0] * n\n        \n        full_edges = set()\n        partial_edges = set()\n        partial_adj = {}\n        partial_adj[1] = collections.defaultdict(set)\n        partial_adj[2] = collections.defaultdict(set)\n        for e in edges:\n            edge_type, u, v = e[0], e[1] - 1, e[2] - 1\n            if edge_type == 3:\n                full_edges.add((u, v))\n            else:\n                partial_edges.add((edge_type, u, v))\n                partial_adj[edge_type][u].add(v)\n                partial_adj[edge_type][v].add(u)\n\n        nb_edges_in_mst = 0\n        for e in full_edges:\n            u, v = e\n            root_u, root_v = find(p, u), find(p, v)\n            if root_u != root_v:\n                union(p, rank, root_u, root_v)\n                nb_edges_in_mst += 1\n        \n        for e in partial_edges:\n            edge_type, v0, v1 = e\n            if find(p, v0) == find(p, v1):\n                continue\n\n            # We have two nodes u and v, u in a fully-connected component A, v in another\n            # fully-connected component B. A disjoint from B and u is partially connected\n            # to v via an edge of `edge_type`. Since we need to reach v from u by both\n            # Alice and Bob, if we can find another node, x, in A that is partially-connected \n            # to v by an edge of `needed_edge_type`, then we have edges of both types that\n            # we can use to reach v from u. (use `edge_type` from u->v, or use `needed_edge_type`\n            # from u->x->v). Since the situation is symmetric, we'll need to test with roles of\n            # u and v swapped\n            needed_edge_type = 2 if edge_type == 1 else 2\n            for pair in [(v0, v1), (v1, v0)]:\n                u, v = pair\n                root_x = None\n                for x in partial_adj[needed_edge_type][v]:\n                    if find(p, x) == find(p, u):\n                        # We've found a node x in A fully connected to u, AND it's partially connected\n                        # to v via the `needed_edge_type`\n                        root_x = find(p, x)\n                        break\n                if root_x != None:\n                    root_u = find(p, u)\n                    root_v = find(p, v)\n                    union(p, rank, root_x, root_v)\n                    union(p, rank, root_u, root_v)\n                    nb_edges_in_mst += 2\n                    break\n\n        uniq_roots = set()\n        for u in range(len(p)):\n            uniq_roots.add(find(p, u))\n        if len(uniq_roots) != 1:\n            return -1  \n        \n        return len(edges) - nb_edges_in_mst\n            \n                \n            \n        \n", "class UnionFind:\n    def __init__(self, n):\n        self.parents = [i for i in range(n)]\n        \n    def union(self, index1, index2):\n        root1 = self.find(index1)\n        root2 = self.find(index2)\n        if root1 == root2:\n            return 0\n        self.parents[root2] = root1\n        return 1\n    \n    def find(self, index):\n        if self.parents[index] != index:\n            self.parents[index] = self.find(self.parents[index])\n        return self.parents[index]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1, uf2 = UnionFind(n), UnionFind(n)\n        count_delete = 0\n        count_union1, count_union2 = 0, 0\n        \n        for i, (weight, node1, node2) in enumerate(edges):\n            if weight == 3:\n                is_union = uf1.union(node1 - 1, node2 - 1)\n                # is_union2 = uf2.union(node1 - 1, node2 - 1)\n                # is_union = is_union1 and is_union2\n                count_union1 += is_union\n                count_union2 += is_union\n                count_delete += 1 - is_union\n        uf2.parents = copy.deepcopy(uf1.parents)\n        count_union2 = count_union1\n        \n        for i, (weight, node1, node2) in enumerate(edges):\n            if weight == 1:\n                is_union = uf1.union(node1 - 1, node2 - 1)\n                count_union1 += is_union\n                count_delete += 1 - is_union\n            elif weight == 2:\n                is_union = uf2.union(node1 - 1, node2 - 1)\n                count_union2 += is_union\n                count_delete += 1 - is_union\n\n        if count_union1 != n - 1 or count_union2 != n - 1:\n            return -1\n        return count_delete\n                \n            \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        d = [[], [], []]\n        for type, u, v in edges:\n            d[type - 1].append((u - 1, v - 1))\n            \n        parent = list(range(n))\n        def find(x, l):\n            if x != l[x]:\n                l[x] = find(l[x], l)\n            return l[x]\n        \n        cnt = 0\n        for u, v in d[-1]:\n            ru, rv = find(u, parent), find(v, parent)\n            if ru != rv:\n                parent[ru] = rv\n                cnt += 1\n                \n        alice = [num for num in parent]\n        for u, v in d[0]:\n            ru, rv = find(u, alice), find(v, alice)\n            if ru != rv:\n                alice[ru] = rv\n                cnt += 1\n                \n        ra = find(0, alice)\n        for i in range(n):\n            if find(i, alice) != ra:\n                return -1\n                \n        bob = [num for num in parent]\n        for u, v in d[1]:\n            ru, rv = find(u, bob), find(v, bob)\n            if ru != rv:\n                bob[ru] = rv\n                cnt += 1\n                \n        rb = find(0, bob)\n        for i in range(n):\n            if find(i, bob) != rb:\n                return -1\n            \n        return len(edges) - cnt", "import bisect\nimport functools\nfrom typing import List\n\n\nclass Solution:\n  def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n\n    result = 0\n    e = [[_, u-1, v -1] for _, u, v in edges]\n    a = [i for i in range(n)]\n    b = [i for i in range(n)]\n    def find(p, x):\n      p[x] = p[x] if p[x] == x else find(p, p[x])\n      return p[x]\n\n    def union(p, a, b):\n      find_a = find(p, a)\n      find_b = find(p, b)\n      if find_a == find_b:\n        return 1\n      p[find_a] = find_b\n      return 0\n\n    same = 0\n    for type, u, v in e:\n      if type == 3:\n        same += union(a, u, v) | union(b, u, v)\n\n    for type, u, v in e:\n      if type == 1:\n        same += union(a, u, v)\n      if type == 2:\n        same += union(b, u, v)\n\n    all_a = all(find(a, 0) == find(a, x) for x in a)\n    all_b = all(find(b, 0) == find(b, x) for x in b)\n    if all_a and all_b:\n      return same\n\n    return -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        f = {}\n        \n        def find(x):\n            f.setdefault(x,x)\n            if x!=f[x]:\n                f[x]= find(f[x])\n            return f[x]\n        \n        def union(x,y):\n            x = find(x)\n            y = find(y)\n            if x==y:\n                return False\n            f[x]=y\n            return True\n        \n        res, e1, e2 = 0,0,0\n        \n        for t,u,v in edges:\n            if t==3:\n                if union(u,v):\n                    e1+=1\n                    e2+=1\n                else:\n                    res+=1\n                    \n        copy_f = f.copy()\n        for t,u,v in edges:\n            if t==1:\n                if union(u,v):\n                    e1+=1\n                else:\n                    res+=1\n                    \n        f = copy_f\n        for t,u,v in edges:\n            if t==2:\n                if union(u,v):\n                    e2+=1\n                else:\n                    res+=1\n        \n        return res if e1==e2==n-1 else -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, e: List[List[int]]) -> int:\n        def union(UF, u, v):\n            pu, pv = find(UF, u), find(UF, v)\n            if pu != pv: UF[pv] = pu\n        def find(UF, u):\n            if UF[u] != u: UF[u] = find(UF, UF[u])\n            return UF[u]         \n        def check(UF, t):            \n            UF = UF.copy()\n            for tp, u, v in e:\n                if tp != t: continue\n                pu, pv = find(UF, u), find(UF, v)\n                if pu == pv: self.ans += 1\n                else: union(UF, u, v)\n            return len(set(find(UF, u) for u in UF)) == 1\n        \n        self.ans, UF = 0, {k: k for k in range(1, n+1)}        \n        for t, u, v in e:\n            if t != 3: continue\n            pu, pv = find(UF, u), find(UF, v)\n            if pu == pv: self.ans += 1\n            else: union(UF, u, v)        \n        if not check(UF, 1): return -1\n        if not check(UF, 2): return -1        \n        return self.ans                        ", "class DSU:\n    def __init__(self, N):\n        self.par = list(range(N))\n        self.rnk = [0] * N\n        self.count = N\n        \n    def find(self, x):\n        if self.par[x] != x:\n            self.par[x] = self.find(self.par[x])\n        return self.par[x]\n    \n    def union(self, x, y):\n        xr, yr = list(map(self.find, (x, y)))\n        if xr == yr: return False\n        self.count -= 1\n        if self.rnk[xr] < self.rnk[yr]:\n            xr, yr = yr, xr\n        if self.rnk[xr] == self.rnk[yr]:\n            self.rnk[xr] += 1\n        self.par[yr] = xr\n        return True\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        uf_a, uf_b = DSU(n), DSU(n)\n        \n        ans = 0\n        edges.sort(reverse=True)\n        for t, u, v in edges:\n            u, v = u - 1, v - 1\n            if t == 3:\n                ans += not (uf_a.union(u, v) and uf_b.union(u, v))\n            elif t == 2:\n                ans += not uf_b.union(u, v)\n            else:\n                ans += not uf_a.union(u, v)\n                \n        return ans if uf_a.count == 1 and uf_b.count == 1 else -1\n", "from copy import deepcopy\nfrom collections import Counter\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        edges.sort(reverse = True)\n        \n        graph = [i for i in range(n)]\n        \n        def p(i,graph):\n            level = 0\n            while i != graph[i]:\n                i = graph[i]\n                level += 1\n            return i,level\n        \n        i = 0\n        res = 0\n        g = graph\n        graph_a = graph_b = None\n        \n        while i < len(edges):\n            t,a,b = edges[i]\n            if t == 2 and (i == 0 or edges[i-1][0] != 2):\n                graph_b = deepcopy(graph)\n                g = graph_b\n            if t == 1 and (i == 0 or edges[i-1][0] != 1):\n                graph_a = deepcopy(graph)\n                g = graph_a\n            ap,al = p(a-1,g)\n            bp,bl = p(b-1,g)\n            if ap == bp: res += 1\n            elif ap == a-1: g[ap] = bp\n            elif bp == b-1: g[bp] = ap\n            elif al < bl: g[ap] = bp\n            else: g[bp] = ap\n            i += 1\n            \n        if not graph_a:\n            graph_a = deepcopy(graph)\n        if not graph_b:\n            graph_b = deepcopy(graph)\n        \n        for i in range(n):\n            graph_a[i] = p(i,graph_a)[0]\n            graph_b[i] = p(i,graph_b)[0]\n            \n        a = Counter(graph_a)\n        b = Counter(graph_b)\n        if len(a) > 1 or len(b) > 1: return -1\n        return res", "class DSU:\n    def __init__(self, N):\n        self.parent = list(range(N+1))\n        self.edges = 0\n\n    def find(self, x):\n        if x != self.parent[x]:\n            # \u8def\u5f84\u5b8c\u5168\u538b\u7f29\n            self.parent[x] = self.find(self.parent[x])\n\n        return self.parent[x]\n\n    def union(self, x, y):\n        root1 = self.find(x)\n        root2 = self.find(y)\n        # \u8fd9\u4e00\u53e5\u975e\u5fc5\u987b\uff0c\u53ea\u662f\u6ee1\u8db3\u6b64\u9898 \u9700\u8981\u5220\u9664\u6b64\u6761\u8fb9\n        if root1 == root2:\n            return 1\n        \n        self.parent[root2] = root1\n        self.edges += 1\n        \n        return 0\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        ans = 0\n        \n        A = DSU(n)\n        B = DSU(n)\n        \n        for t, x, y in edges:\n            if t != 3:\n                continue\n            ans += A.union(x, y)\n            B.union(x, y)\n        \n        for t, x, y in edges:\n            if t == 3:\n                continue\n            # print(t, x, y)\n            d = A if t == 1 else B\n            ans += d.union(x, y)\n            \n        return ans if A.edges == n - 1 and B.edges == n - 1 else -1\n            \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        res, e0, e1 = 0, 0, 0\n        self.root = [i for i in range(n+1)]\n        def find(x):\n            if x != self.root[x]:\n                self.root[x] = find(self.root[x])\n            return self.root[x]\n        \n        def uni(x, y):\n            x, y = find(x), find(y)\n            if x==y:\n                return 1\n            self.root[x] = y\n            return 0\n        \n        for t,i,j in edges:\n            if t == 3:\n                if uni(i,j):\n                    res += 1\n                else:\n                    e0 += 1\n                    e1 += 1\n                    \n        root0 = self.root[:]\n        for t,i,j in edges:\n            if t == 1:\n                if uni(i, j):\n                    res += 1\n                else:\n                    e0 += 1\n                    \n        self.root = root0\n        for t,i,j in edges:\n            if t==2:\n                if uni(i,j):\n                    res += 1\n                else:\n                    e1 += 1\n                    \n        return res if e1 == e0 == (n-1) else -1\n                    \n", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        # build graph, use type 3 first\n        # then do alice and bob separately\n        # have dsu parents to build up alice and bob\n        \n        parentsA = list(range(n))\n        parentsB = list(range(n))\n        \n        def find(parents, a):\n            while a != parents[a]:\n                parents[a] = parents[parents[a]]\n                a = parents[a]\n            return a\n            \n        def union(parents, a, b):\n            a = find(parents, a)\n            b = find(parents, b)\n            parents[a] = b\n        \n        typeA, typeB = [], []\n        tree1, tree2, res = 0,0,0\n        \n        for t, u, v in edges:\n            u, v = u-1, v-1 # make zero indexed, easier for UF\n            if t == 3:\n                if find(parentsA, u) != find(parentsA, v):\n                    tree1 += 1\n                    tree2 += 1\n                    union(parentsA, u, v)\n                    union(parentsB, u, v)\n                else:\n                    res += 1\n            elif t == 2:\n                typeB.append((u, v))\n            elif t == 1:\n                typeA.append((u, v))\n                \n        # now do type1 and 2 separately\n        for u,v in typeA:\n            if find(parentsA, u) != find(parentsA, v):\n                tree1 += 1\n                union(parentsA, u, v)\n            else:\n                res += 1\n        \n        for u,v in typeB:\n            if find(parentsB, u) != find(parentsB, v):\n                tree2 += 1\n                union(parentsB, u, v)\n            else:\n                res += 1\n        \n        if tree1 == n-1 and tree2 == n-1:\n            return res\n        else:\n            return -1", "class Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        class DSU:\n            def __init__(self, n):\n                self.edge_count = 0\n                self.parent = [i for i in range(n + 1)]\n\n            def find(self, x):\n                if x != self.parent[x]:\n                    self.parent[x] = self.find(self.parent[x])\n                return self.parent[x]\n\n            def union(self, u, v):\n                pu, pv = self.find(u), self.find(v)\n                if pu != pv:\n                    self.parent[pv] = pu\n                    self.edge_count += 1\n                    return 0\n                return 1\n            \n            def get_edge_count(self):\n                return self.edge_count\n        \n        A, B = DSU(n), DSU(n)\n\n        ans = 0\n        for typ, u, v in edges:\n            if typ != 3: continue\n            ans += A.union(u, v)\n            B.union(u, v)\n        # print(A.get_edge_count(), B.get_edge_count())    \n        for typ, u, v in edges:\n            if typ == 3: continue\n            if typ == 1:\n                ans += A.union(u, v)\n            elif typ == 2:\n                ans += B.union(u, v)\n        # print(A.get_edge_count(), B.get_edge_count())\n        if A.get_edge_count() == n - 1 and B.get_edge_count() == n - 1:\n            return ans\n        else:\n            return -1", "class Solution:\n    def add_edge(self,parent,cnt,x,y):\n        xx = self.parents(parent,x)\n        yy = self.parents(parent,y)\n        if(cnt[xx] < cnt[yy]):\n            parent[xx]=yy\n            cnt[yy]+=1\n        else:\n            parent[yy]=xx\n            cnt[xx]+=1\n    def parents(self,parent,ch):\n        if(parent[ch]==ch):\n            return ch\n        else:\n            xy = self.parents(parent,parent[ch])\n            parent[ch]=xy\n            return xy\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        parent = (n+1)*[0]\n        ans=0\n        cnt = (n+1)*[0]\n        for i in range(n+1):\n            parent[i]=i\n        edges.sort(reverse= True)\n        for j in range(len(edges)):\n            i = edges[j]\n            if(i[0]!=3):\n                break\n            if(self.parents(parent,i[1])!=self.parents(parent,i[2])):\n                self.add_edge(parent,cnt,i[1],i[2])\n            else:\n                ans+=1\n        bob = parent.copy()\n        bob_cnt = cnt.copy()\n        for k in range(j,len(edges)):\n            i = edges[k]\n            if(i[0]!=2):\n                break\n            if(self.parents(bob,i[1])!=self.parents(bob,i[2])):\n                self.add_edge(bob,bob_cnt,i[1],i[2])\n            else:\n                ans+=1\n        for l in range(k,len(edges)):\n            i = edges[l]\n            if(i[0]!=1):\n                break\n            if(self.parents(parent,i[1])!=self.parents(parent,i[2])):\n                self.add_edge(parent,cnt,i[1],i[2])\n            else:\n                ans+=1\n        rn = 0\n        for i in range(1,n+1):\n            if(parent[i]==i):\n                rn+=1\n        if(rn>1):\n            return -1\n        rn = 0\n        for i in range(1,n+1):\n            if(bob[i]==i):\n                rn+=1\n        if(rn>1):\n            return -1\n        return ans", "from collections import defaultdict\n\nclass UnionFind:\n    \n    def __init__(self, n):\n        self._id = list(range(n))\n        self._sz = [1] * n\n        self.cc = n  # connected components\n\n    def _root(self, i):\n        j = i\n        while (j != self._id[j]):\n            self._id[j] = self._id[self._id[j]]\n            j = self._id[j]\n        return j\n\n    def find(self, p, q):\n        return self._root(p) == self._root(q)\n\n    def union(self, p, q):\n        i = self._root(p)\n        j = self._root(q)\n        if i == j:\n            return\n        if (self._sz[i] < self._sz[j]):\n            self._id[i] = j\n            self._sz[j] += self._sz[i]\n        else:\n            self._id[j] = i\n            self._sz[i] += self._sz[j]\n        self.cc -= 1\n        \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        \n        graph=defaultdict(list)\n        class1=UnionFind(n)\n        class2=UnionFind(n)\n        \n        for cat, start, end in edges:\n            graph[cat].append((start-1,end-1))\n            \n        class1=UnionFind(n)\n        class2=UnionFind(n)\n        \n        \n        ans=0\n      \n        for start,end in graph[3]:\n            cur1,cur2=class1.cc,class2.cc\n            class1.union(start,end)\n            class2.union(start,end)\n            if class1.cc==cur1 and class2.cc==cur2:\n                ans+=1\n            \n        for start,end in graph[1]:\n            cur1=class1.cc\n            class1.union(start,end)\n            if class1.cc==cur1:\n                ans+=1\n                \n        for start,end in graph[2]:\n            cur2=class2.cc\n            class2.union(start,end)\n            if class2.cc==cur2:\n                ans+=1  \n                \n        if class1.cc==1 and class2.cc==1:\n            return ans\n        else:\n            return -1\n    \n    \n    \n            \n", "class UnionFind:\n    def __init__(self, n):\n        self.parent = list(range(n))\n        self.rank = [1] * n\n        self.count = n\n\n    def find(self, i):\n        if self.parent[i] != i:\n            self.parent[i] = self.find(self.parent[i])\n        return self.parent[i]\n\n    def union(self, x, y):\n        px = self.find(x)\n        py = self.find(y)\n        if px == py:\n            return False\n        if self.rank[px] > self.rank[py]:\n            px, py = py, px\n        self.parent[px] = py\n        if self.rank[px] == self.rank[py]:\n            self.rank[py] += 1\n        self.count -= 1\n        return True\n\n    def united(self):\n        return self.count == 1\n\n    \nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        edges.sort(key=lambda x:-x[0])\n        alice = UnionFind(n)\n        bob = UnionFind(n)\n        added = 0\n        for t, a, b in edges:\n            a -= 1\n            b -= 1\n            if t == 3:\n                added += 1 if alice.union(a, b) | bob.union(a, b) else 0\n            elif t == 1:\n                added += 1 if alice.union(a, b) else 0\n            elif t == 2:\n                added += 1 if bob.union(a, b) else 0\n\n        return len(edges) - added if alice.united() and bob.united() else -1\n", "import copy\n\nclass DJ_DS():\n    def __init__(self, n):\n        self.n = n\n        self.parent = [i for i in range(n)]\n        self.rank = [0 for i in range(n)]\n        self.nb_edges = 0\n    \n    def find_parent(self,i): # faster with path compression\n        while self.parent[i] != i:\n            i = self.parent[i]\n        return i\n        \n    def union(self,i,j):\n        p_i = self.find_parent(i)\n        p_j = self.find_parent(j)\n        \n        if p_i != p_j:\n            self.nb_edges += 1\n            if self.rank[p_i] < self.rank[p_j]:\n                self.parent[p_i] = p_j\n            else:\n                self.parent[p_j] = p_i\n                if self.rank[p_i] == self.rank[p_j]:\n                    self.rank[p_i] += 1\n                \n    def perform_merge(self, edges):\n        for [u,v] in edges:\n            self.union(u,v)\n            \n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        nb_edges = len(edges)\n        # list of edges of each color individually\n        type1, type2, type3 = [], [], []\n        for [t,u,v] in edges:\n            if t == 1:\n                type1.append([u-1,v-1])\n            elif t == 2:\n                type2.append([u-1,v-1])\n            else:\n                type3.append([u-1,v-1])\n        \n        # Count nb_edges with type 3 only in max forest\n        dj_3 = DJ_DS(n)\n        dj_3.perform_merge(type3)\n        sol_3 = dj_3.nb_edges\n        dj_1 = copy.deepcopy(dj_3)\n        dj_2 = copy.deepcopy(dj_3)\n        \n        # From type 3 forest add edges from type 1 to see if spanning tree, if not return -1\n        dj_1.perform_merge(type1)\n        if dj_1.nb_edges < n-1:\n            return -1\n        \n        # From type 3 forest add edges from type 2 to see if spanning tree, if not return -1\n        dj_2.perform_merge(type2)\n        if dj_2.nb_edges < n-1:\n            return -1\n        \n        return (nb_edges - (sol_3 + 2 * (n-1 - sol_3)))", "class UnionFind:\n    def __init__(self, n):\n        self.parents = [i for i in range(n)]\n        \n    def union(self, index1, index2):\n        root1 = self.find(index1)\n        root2 = self.find(index2)\n        if root1 == root2:\n            return 0\n        self.parents[root2] = root1\n        return 1\n    \n    def find(self, index):\n        if self.parents[index] != index:\n            self.parents[index] = self.find(self.parents[index])\n        return self.parents[index]\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        uf1, uf2 = UnionFind(n), UnionFind(n)\n        count_delete = 0\n        count_union1, count_union2 = 0, 0\n        \n        for i, (weight, node1, node2) in enumerate(edges):\n            if weight == 3:\n                is_union1 = uf1.union(node1 - 1, node2 - 1)\n                is_union2 = uf2.union(node1 - 1, node2 - 1)\n                is_union = is_union1 and is_union2\n                \n                count_union1 += is_union1\n                count_union2 += is_union2\n                count_delete += 1 - is_union\n        \n        for i, (weight, node1, node2) in enumerate(edges):\n            if weight == 1:\n                is_union = uf1.union(node1 - 1, node2 - 1)\n                count_union1 += is_union\n                count_delete += 1 - is_union\n            elif weight == 2:\n                is_union = uf2.union(node1 - 1, node2 - 1)\n                count_union2 += is_union\n                count_delete += 1 - is_union\n\n        if count_union1 != n - 1 or count_union2 != n - 1:\n            return -1\n        return count_delete\n                \n            \n", "class UF:\n    def __init__(self, n):\n        self.count = n\n        self.parents = [0]*(n+1)\n        for i in range(1+n):\n            self.parents[i]=i\n    def find(self, x):\n        if x != self.parents[x]:\n            self.parents[x] = self.find(self.parents[x])\n        return self.parents[x]\n    def union(self,x,y):\n        p_x = self.find(x)\n        p_y = self.find(y)\n        if p_x == p_y:\n            return False\n        self.parents[p_x] = p_y\n        self.count -= 1\n        return True\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        ufa = UF(n)\n        ufb = UF(n)\n        res = 0\n        for t, u, v in edges:\n            if t == 3:\n                flag1 = ufa.union(u,v)\n                flag2 = ufb.union(u,v)\n                if not flag1 and not flag2:\n                    res += 1\n        for t, u, v in edges:\n            if t ==1:\n                if not ufa.union(u, v):\n                    res += 1\n            if t ==2:\n                if not ufb.union(u, v):\n                    res += 1\n        print((ufa.count, ufb.count))\n        if ufa.count != 1 or ufb.count != 1:\n            return -1\n        return res\n                    \n", "class DSU:\n    def __init__(self, N):\n        self.parent = list(range(N))\n        self.size = [1] * N\n\n    def find(self, x):\n        if self.parent[x] != x:\n            self.parent[x] = self.find(self.parent[x])\n        return self.parent[x]\n    \n    def union(self, x, y):\n        xr = self.find(x)\n        yr = self.find(y)\n        \n        if xr == yr: return\n        \n        if self.size[xr] >= self.size[yr]:\n            self.size[xr] += self.size[yr]\n            self.size[yr] = self.size[xr]\n            self.parent[yr] = xr\n        else:\n            self.size[yr] += self.size[xr]\n            self.size[xr] = self.size[yr]\n            self.parent[xr] = yr\n\n\nclass Solution(object):\n    \n    def dfs(self, u, allowed_types, visited, graph):\n        visited.add(u)\n        for v in graph[u]:\n            if v not in visited and (allowed_types[0] in graph[u][v] or allowed_types[1] in graph[u][v]):\n                self.dfs(v, allowed_types, visited, graph)\n            \n    \n    def maxNumEdgesToRemove(self, N, edges):\n        \\\"\\\"\\\"\n        :type n: int\n        :type edges: List[List[int]]\n        :rtype: int\n        \\\"\\\"\\\"\n        dsu_alice = DSU(N)\n        dsu_bob = DSU(N)\n        count_edges = 0\n        \n        for t, u, v in edges:\n            u -= 1\n            v -= 1\n            if t == 3:\n                pu, pv = dsu_bob.find(u), dsu_bob.find(v)\n                if pu != pv:\n                    dsu_alice.union(u, v)\n                    dsu_bob.union(u, v)\n                    count_edges += 1\n        \n        for t, u, v in edges:\n            u -= 1\n            v -= 1\n            if t == 1:\n                pu, pv = dsu_alice.find(u), dsu_alice.find(v)\n                if pu != pv:\n                    dsu_alice.union(u, v)\n                    count_edges += 1\n            elif t == 2:\n                pu, pv = dsu_bob.find(u), dsu_bob.find(v)\n                if pu != pv:\n                    dsu_bob.union(u, v)\n                    count_edges += 1\n                    \n        try:\n            dsu_bob.size.index(N)\n            dsu_alice.size.index(N)\n        except:\n            return -1\n        \n        return len(edges) -  count_edges\n        \n        ", "class DisjointSet:\n    def __init__(self, number_of_sites):\n        self.parent = [i for i in range(number_of_sites+1)]\n        self.children_site_count = [1 for _ in range(number_of_sites+1)]\n        self.component_count = number_of_sites\n\n    def find_root(self, site):\n        root = site\n        while root != self.parent[root]:\n            root = self.parent[root]\n        while site != root:\n            site, self.parent[site] = self.parent[site], root\n        return root\n\n    def is_connected(self, site_1, site_2):\n        return self.find_root(site_1) == self.find_root(site_2)\n\n    def union(self, site_1, site_2):\n        site_1_root = self.find_root(site_1)\n        site_2_root = self.find_root(site_2)\n        if site_1_root == site_2_root:\n            return False\n\n        if self.children_site_count[site_1_root] < self.children_site_count[site_2_root]:\n            self.parent[site_1_root] = site_2_root\n            self.children_site_count[site_2_root] += self.children_site_count[\n                site_1_root]\n        else:\n            self.parent[site_2_root] = site_1_root\n            self.children_site_count[site_1_root] += self.children_site_count[\n                site_2_root]\n        self.component_count -= 1\n        return True\n\n\nclass Solution:\n    def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:\n        alice_disjoint_set = DisjointSet(n)\n        bob_disjoint_set = DisjointSet(n)\n\n        TYPE_OF_COMMON_EDGES = 3\n        TYPE_OF_ALICE_EDGES = 1\n        TYPE_OF_BOB_EDGES = 2\n\n        common_edges = filter(lambda edge: edge[0] == TYPE_OF_COMMON_EDGES, edges)\n        alice_edges = filter(lambda edge: edge[0] == TYPE_OF_ALICE_EDGES, edges)\n        bob_edges = filter(lambda edge: edge[0] == TYPE_OF_BOB_EDGES, edges)\n\n        redundant = 0\n        for _, u, v in common_edges:\n            if (not alice_disjoint_set.union(u, v)) or (not bob_disjoint_set.union(u, v)):\n                redundant += 1\n\n        for _, u, v in bob_edges:\n            if not bob_disjoint_set.union(u,v):\n                redundant += 1\n                \n        for _, u, v in alice_edges:\n            if not alice_disjoint_set.union(u, v):\n                redundant += 1\n        \n        return redundant if alice_disjoint_set.component_count == 1 and bob_disjoint_set.component_count == 1 else -1"]