Java, Union Find(compress path+rank) once, check type 3 edges, then check others
\nclass Solution {\n    public int maxNumEdgesToRemove(int n, int[][] edges) {\n        int[] componentsAlice=new int[n+1];\n        int[] componentsBob=new int
```\nclass Solution {\n    public int maxNumEdgesToRemove(int n, int[][] edges) {\n        int[] componentsAlice=new int[n+1];\n        int[] componentsBob=new int[n+1];\n        // to make union find tree balance, avoid the worst case where the tree is a single linked list \n        int[] sizeAlice=new int[n+1];\n        int[] sizeBob=new int[n+1];\n        int numsAlice=n,numsBob=n;\n        int cycleAlice=0,cycleBob=0,commonCycle=0;\n        // initialize union find related variables \n        for(int i=1;i<=n;i++)\n        {    \n            componentsAlice[i]=i;\n            componentsBob[i]=i;\n            sizeAlice[i]=1;\n            sizeBob[i]=1;\n        }\n        // this step is very important!!!. we need to firstly check edges that can be traversed by Alice and Bob. \n        // we use common edges to connect all nodes, then we use type 1/2 to connect nodes for Alice/Bob. So all edges that cause cycles in union find can be removed. \n        Arrays.sort(edges,(a,b)->(b[0]-a[0]));\n        for(int[] edge:edges){\n            if(edge[0]==3){\n                boolean resAlice=union(edge[1],edge[2],componentsAlice,sizeAlice);\n                boolean resBob=union(edge[1],edge[2],componentsBob,sizeBob);\n                if(resAlice&&resBob) commonCycle++;\n                else {\n                    if(resAlice) cycleAlice++;\n                    else numsAlice--;\n                    if(resBob) cycleBob++;\n                    else numsBob--;\n                } \n            } else if(edge[0]==2){\n                if(union(edge[1],edge[2],componentsBob,sizeBob))\n                    cycleBob++;\n                else numsBob--;\n            } else {\n                if(union(edge[1],edge[2],componentsAlice,sizeAlice))\n                    cycleAlice++;\n                else numsAlice--;\n            }\n        }\n        if(numsAlice>1||numsBob>1) return -1;\n        return cycleAlice+cycleBob+commonCycle;\n    }\n    \n    int findParent(int n,int[] components){\n        while(components[n]!=n){\n            // compress path\n            n=components[components[n]];\n        }\n        return n;\n    }\n    \n    boolean union(int a,int b,int[] components,int[] size){\n        int parentA=findParent(a,components);\n        int parentB=findParent(b,components);\n        // node a is already connected with node b. so this edge is redundant. \n        if(parentA==parentB) return true;\n        // use size to determine which node can be parent \n        if(size[parentA]>size[parentB]){\n            components[parentB]=parentA;\n            size[parentA]+=size[parentB];\n        } else{\n            components[parentA]=parentB;\n            size[parentB]+=size[parentA];\n        }\n        return false;\n    }\n}\n```
[]