K-th Largest Connected Components —— atcoder372
11/4更新,今天学了并查集,发现这道题其实就是构造了一个并查集,amazing啊!
问题描述 有一个无向图,它有 N 个顶点和 0 条边。顶点编号为 1 至 N。
有Q次询问,每次询问是以下两种操作中的一种:
类型一:给定格式为 1 u v 的输入。为节点 u 和 v 之间建立一条边。
类型二:给定格式为 2 v k 的输入。打印节点 v 可达的节点中第 k 大的节点的值。
总结反思 一开始错误理解了题目,以为只在节点 v 的邻居节点之间找,后来学习了一下其他人提交的正确答案才知道,原来是要在 v 节点所有可达的节点中间去查找第 k 大的节点值。
这就有两个难点:
在两个节点建立联系之后如何更新各自可达的节点?——难点在于,对于 u 节点来说,不仅要把 v 节点纳入可达节点,v 节点可达的所有节点也都要纳入进来,怎样去做这件事情?如果按照常规的方式,似乎不太好做。
怎么用较小的时间复杂度找到这第 k 大的节点?——我一开始想到的方式是用 List<List<Integer>>
来存邻接表,在每加入一条边时都重新对该节点的所有邻居节点重新排序,这个时间复杂度必然是不满足要求的。
解决第一个问题的方法是——用 parents 数组来存储每个节点所属的图的根节点信息。当然,图是没有根节点的,这里只是把每一个图中的第一个节点作为了根节点,同时把这个根节点的值作为 key 来找到这整个图的所有节点。同时配套的还有 findParents
方法和 union
方法前者用来递归查找当前节点的所在图的根节点,当 u 和 v 节点的根节点不一致时,这个时候就需要更新可达信息了,更新的方法就是union
方法,那么把哪个图合并到另一个图中呢?答案是把节点数少的合并到节点数多的图中,这样插入操作会少一些,为了保存这个信息,就有了 sizes 数组。
解决第二个问题的方法是——用 TreeSet
来保存图的节点信息,插入时的维护代价小,同时查找的代价相比 List
和 HashMap
也不是特别高。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 import java.util.*;public class KthLargestConnectedComponents { public static void main (String[] args) { Scanner sc = new Scanner (System.in); int N = sc.nextInt(); int Q = sc.nextInt(); int [] parents = new int [N + 1 ]; int [] sizes = new int [N + 1 ]; Arrays.fill(parents, -1 ); Arrays.fill(sizes, 1 ); Map<Integer, TreeSet<Integer>> graph = new HashMap <>(); for (int i = 1 ; i <= N; i++) { TreeSet<Integer> vertices = new TreeSet <>(Collections.reverseOrder()); vertices.add(i); graph.put(i, vertices); } for (int i = 0 ; i < Q; i++) { int op = sc.nextInt(); if (op == 1 ) { int u = sc.nextInt(); int v = sc.nextInt(); int rootU = findRoot(parents, u); int rootV = findRoot(parents, v); if (rootU != rootV) { if (sizes[rootU] < sizes[rootV]) { int temp = rootU; rootU = rootV; rootV = temp; } union(parents, sizes, rootU, rootV); graph.get(rootU).addAll(graph.get(rootV)); graph.remove(rootV); } } else if (op == 2 ) { int node = sc.nextInt(); int k = sc.nextInt(); TreeSet<Integer> vertices = graph.get(findRoot(parents, node)); if (k > vertices.size()) { System.out.println(-1 ); } else { Iterator<Integer> iterator = vertices.iterator(); int index = 1 ; while (iterator.hasNext()) { Integer next = iterator.next(); if (index == k) { System.out.println(next); break ; } index++; } } } } } private static void union (int [] parents, int [] sizes, int rootU, int rootV) { parents[rootV] = rootU; sizes[rootU] += sizes[rootV]; } private static int findRoot (int [] parents, int node) { if (parents[node] == -1 ) { return node; } return parents[node] = findRoot(parents, parents[node]); } }