import java.io.OutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.HashMap; import java.util.ArrayList; import java.util.HashSet; import java.util.StringTokenizer; import java.util.Map; import java.io.OutputStreamWriter; import java.io.OutputStream; import java.io.PrintWriter; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.stream.Stream; import java.io.Writer; import java.io.BufferedReader; import java.util.Collections; import java.io.InputStream; /** * Built using CHelper plug-in * Actual solution is at the top * * @author lewin */ public class special_cycle_lewin { public static void main(String[] args) { InputStream inputStream = System.in; OutputStream outputStream = System.out; InputReader2 in = new InputReader2(inputStream); OutputWriter out = new OutputWriter(outputStream); SpecialCycle2 solver = new SpecialCycle2(); solver.solve(1, in, out); out.close(); } static class SpecialCycle2 { List[] graph; List[] tgraph; int[] sdeg; int[] par; int maxdeg; boolean[] vis; boolean[] is_deleted; List nodes; boolean[][] deleted_edge; List[][] expanded; int[] stack; int sidx; boolean[] active_node; boolean[] must_have; void dfs0(int node) { if (vis[node]) return; vis[node] = true; nodes.add(node); maxdeg = Math.max(maxdeg, sdeg[node]); for (SpecialCycle2.Edge nxt : graph[node]) { if (!vis[nxt.to]) { dfs0(nxt.to); } } } boolean is_active(int from, int to) { return !is_deleted[from] && !is_deleted[to] && !deleted_edge[from][to]; } public void solve(int testNumber, InputReader2 in, OutputWriter out) { /** * First, we'll reduce the problem to one where no special edges share a node * * Let's look at only connected components of special edges * - If any are a cycle, return that cycle * - For each path p1, p2, ..., pk, delete nodes p2, ..., p_{k-1}, and connect p1 to pk (remember to expand this when reconstructing the answer) * - Any isolated nodes can be left alone * - For all other components, you can delete all nodes of that component (no cycle can ever touch a node from one of these components) * * Next, we'll reduce this to a problem where components are 2-connected * - If any special edge is a bridge, delete the two endpoints of this edge from the graph, and recurse * - If any normal edge is a bridge, just delete that edge * * Now, there are no more bridges, and special edges share no nodes in common. * We claim a solution always exists at this point if there is 2-connected component with size > 1 (proof at bottom) * * To construct an actual solution, we can do the following algorithm: * * - First remove all special edges and nodes that are connected to special edges. If there is a cycle in remaining edges, we can return it. * - Otherwise, add special edges and their corresponding nodes one by one until the graph is 2-connected. The last special edge we added must be part of some solution, so we remove all edges except for already fixed special edges, and repeat this process.This takes O(n^2) iterations to find which special edges are part of some solution. * - Now, we can try to delete non-special edges one by one while the graph remains 2 connected. The edges that remain after all iterations is the answer. This takes O(m) iterations. * * The 2-edge connected check is O(n+m), so overall running time is (O(n^2+m)*(n+m)) * ********************* * * For the proof of why a solution always exists in a 2-connected graph, we do it by induction on the size of the graph and number of special edges. * With zero special edges, the answer is easy. * Let's suppose we're trying to find a solution with i special edges. * * First, we find a solution with the first i-1 special edges (treating the i-th edge as non-special). There are three cases * - The i-th special edge touches this cycle 0 times. We are done * - The i-th special edge touches this cycle 2 times. Taking this edge, and either arc around the cycle give us an answer * - The i-th special edge touches this cycle 1 time. This one is a bit tricky. * * To solve this, contract the cycle into a single node and recursively solve the problem on the i special edges and smaller graph. * There are a few cases from here * - If the new cycle doesn't touch the contracted node, then we're done and can just return this solution * - If the new cycle touches the contracted node, there are two cases, in the expanded graph, it either is a path or a cycle. If it is a cycle, then we are also done (since the i-th special edge touches the contracted node, so there can't be any other special edge touching it in the expanded graph. If it is a path, one of the two arcs must work to form a new cycle. * * This proves there is always a solution (though the runtime of running this proof directly could be exponential in the worst case). We simplified the check to just checking 2-edge-connectivity, so that allows us to find a polynomial time algorithm instead. */ int n = in.nextInt(), m = in.nextInt(), k = in.nextInt(); graph = LUtils.genArrayList(n); sdeg = new int[n]; // read in only special edges first for (int i = 0; i < k; i++) { int a = in.nextInt() - 1, b = in.nextInt() - 1; graph[a].add(new SpecialCycle2.Edge(a, b, true, false)); graph[b].add(new SpecialCycle2.Edge(b, a, true, false)); sdeg[a]++; sdeg[b]++; } // first look at only special components // if any are a cycle, just return that // for all paths p1-p2-...-pk, delete nodes p2...pk-1, and add edge from p1 to pk (remember to expand this in final answer) // for all other components, delete all of those nodes is_deleted = new boolean[n]; vis = new boolean[n]; // first try to find paths expanded = new List[n][n]; for (int i = 0; i < n; i++) { if (!vis[i] && sdeg[i] == 1) { nodes = new ArrayList<>(); maxdeg = 0; dfs0(i); if (maxdeg > 2) { // component is not a path, delete everything for (int x : nodes) is_deleted[x] = true; } else { // it is a path // connect nodes with degree 1, delete all other nodes int start = -1, end = -1; for (int x : nodes) { if (sdeg[x] == 1) { if (start == -1) { start = x; } else { end = x; } } else { is_deleted[x] = true; } graph[x].clear(); } expanded[nodes.get(0)][nodes.get(nodes.size() - 1)] = new ArrayList<>(nodes.subList(1, nodes.size())); Collections.reverse(nodes); expanded[nodes.get(0)][nodes.get(nodes.size() - 1)] = new ArrayList<>(nodes.subList(1, nodes.size())); graph[start].add(new SpecialCycle2.Edge(start, end, true, true)); graph[end].add(new SpecialCycle2.Edge(end, start, true, true)); } } } // process all other components for (int i = 0; i < n; i++) { if (!vis[i]) { nodes = new ArrayList<>(); maxdeg = 0; dfs0(i); if (maxdeg == 0) { // isolated node continue; } else if (maxdeg > 2) { // component is not a cycle, delete everything for (int x : nodes) is_deleted[x] = true; } else { // it is a cycle, just print this out.println(nodes.size()); boolean first = true; for (int x : nodes) { if (!first) out.print(" "); out.print(x + 1); first = false; } out.println(); return; } } } // read in the rest of the edges for (int i = k; i < m; i++) { int a = in.nextInt() - 1, b = in.nextInt() - 1; if (is_deleted[a] || is_deleted[b]) continue; if (expanded[a][b] != null) { out.println(expanded[a][b].size() + 1); out.print((a + 1)); for (int x : expanded[a][b]) out.print(" " + (x + 1)); out.println(); return; } graph[a].add(new SpecialCycle2.Edge(a, b, false, false)); graph[b].add(new SpecialCycle2.Edge(b, a, false, false)); } // find bridges in graph // if any special edge is a bridge, delete those two vertices, and repeat the process of finding bridges // if any other edge is a bridge, just delete that edge deleted_edge = new boolean[n][n]; SpecialCycle2.Check2 c = new SpecialCycle2.Check2(graph, is_deleted, deleted_edge); if (!c.run()) { out.println(-1); return; } is_deleted = c.is_deleted; deleted_edge = c.deleted_edge; List normal_edges = new ArrayList<>(); par = DisjointSets.createSets(n); tgraph = LUtils.genArrayList(n); for (int i = 0; i < n; i++) { if (is_deleted[i]) continue; for (SpecialCycle2.Edge e : graph[i]) { if (is_deleted[e.to] || e.to < i) continue; if (sdeg[e.from] == 0 && sdeg[e.to] == 0 && is_active(e.from, e.to)) { normal_edges.add(e); } } } List special_edges = new ArrayList<>(); for (int i = 0; i < n; i++) { if (is_deleted[i]) continue; for (SpecialCycle2.Edge e : graph[i]) { if (is_deleted[e.to] || e.to < i) continue; if (e.is_special) { special_edges.add(e); } } } // figure out which special edges must be part of the solution must_have = new boolean[special_edges.size()]; active_node = new boolean[n]; int last = special_edges.size(); while (true) { tgraph = LUtils.genArrayList(n); par = DisjointSets.createSets(n); for (int i = 0; i < n; i++) active_node[i] = sdeg[i] == 0; // add all fixed special edges for (int j = last; j < special_edges.size(); j++) { if (must_have[j]) { add_special_to_tgraph(special_edges.get(j)); } } // add all normal edges for (SpecialCycle2.Edge e : normal_edges) { add_to_tgraph(e); } // add special edges until we get a 2-connected graph // last special edge that we add must be part of our solution if (new SpecialCycle2.Check2(tgraph, is_deleted, deleted_edge).run()) break; boolean df = false; for (int j = 0; j < last; j++) { add_special_to_tgraph(special_edges.get(j)); if (new SpecialCycle2.Check2(tgraph, is_deleted, deleted_edge).run()) { must_have[j] = true; last = j; df = true; break; } } if (!df) throw new RuntimeException(); } // now we know what special edges must be in our cycle for (int i = 0; i < n; i++) active_node[i] = sdeg[i] == 0; List ans = new ArrayList<>(); tgraph = LUtils.genArrayList(n); for (int i = 0; i < special_edges.size(); i++) { if (must_have[i]) { SpecialCycle2.Edge e = special_edges.get(i); active_node[e.from] = active_node[e.to] = true; add_to_tgraph(e); ans.add(new SpecialCycle2.Edge(e.from, e.to, true, e.need_expand)); } } normal_edges.clear(); for (int i = 0; i < n; i++) { if (is_deleted[i]) continue; for (SpecialCycle2.Edge e : graph[i]) { if (!e.is_special && i < e.to && is_active(e.from, e.to) && active_node[e.from] && active_node[e.to]) { normal_edges.add(e); add_to_tgraph(e); } } } // delete normal edges one by one. If any normal edge deletion makes graph not 2-connected, it must be part of answer for (int i = 0; i < normal_edges.size(); i++) { SpecialCycle2.Edge e = normal_edges.get(i); deleted_edge[e.from][e.to] = deleted_edge[e.to][e.from] = true; if (!new SpecialCycle2.Check2(tgraph, is_deleted, deleted_edge).run()) { deleted_edge[e.from][e.to] = deleted_edge[e.to][e.from] = false; ans.add(new SpecialCycle2.Edge(e.from, e.to, false, e.need_expand)); } } tgraph = LUtils.genArrayList(n); for (SpecialCycle2.Edge e : ans) add_to_tgraph(e); if (!new SpecialCycle2.Check2(tgraph, is_deleted, deleted_edge).run()) throw new RuntimeException(); Arrays.fill(vis, false); stack = new int[n]; sidx = 0; dfs3(ans.get(0).from, -1, -1); ArrayList res = new ArrayList<>(); for (int i = 1; i < sidx; i++) { SpecialCycle2.Edge cur = SpecialCycle2.Edge.emap.get(stack[i]); if (i == 1) res.add(cur.from); if (cur.need_expand) { res.addAll(expanded[cur.from][cur.to]); } else { res.add(cur.to); } } out.println(res.size()); for (int i = 0; i < res.size(); i++) { if (i != 0) out.print(" "); out.print(res.get(i) + 1); } out.println(); } void dfs3(int node, int par, int pid) { if (vis[node]) return; vis[node] = true; stack[sidx++] = pid; for (SpecialCycle2.Edge next : tgraph[node]) { if (next.to == par) continue; dfs3(next.to, node, next.eidx); } } void add_special_to_tgraph(SpecialCycle2.Edge e) { add_to_tgraph(e); active_node[e.from] = active_node[e.to] = true; for (int src : new int[]{e.from, e.to}) { for (SpecialCycle2.Edge nxt : graph[src]) { if (active_node[nxt.to] && is_active(src, nxt.to) && !nxt.is_special) { add_to_tgraph(nxt); } } } } void add_to_tgraph(SpecialCycle2.Edge e) { tgraph[e.from].add(new SpecialCycle2.Edge(e.from, e.to, e.is_special, e.need_expand)); tgraph[e.to].add(new SpecialCycle2.Edge(e.to, e.from, e.is_special, e.need_expand)); } static class Edge { public static int EIDX = 0; public static Map emap = new HashMap<>(); public int from; public int to; public int eidx; public boolean is_special; public boolean need_expand; public Edge(int from, int to, boolean is_special, boolean need_expand) { this.from = from; this.to = to; this.eidx = EIDX++; emap.put(this.eidx, this); this.is_special = is_special; this.need_expand = need_expand; } public String toString() { return "Edge{" + "from=" + from + ", to=" + to + ", eidx=" + eidx + ", is_special=" + is_special + '}'; } } static class Check2 { int n; int[] low; int[] tin; int[] stack; int sidx; int time; boolean[][] deleted_edge; boolean[] vis; boolean[] is_deleted; List[] graph; HashSet seen; boolean is_active(int from, int to) { return !is_deleted[from] && !is_deleted[to] && !deleted_edge[from][to]; } boolean dfs1(int cur, int par) { if (is_deleted[cur]) return false; vis[cur] = true; low[cur] = tin[cur] = time++; stack[sidx++] = cur; boolean changed = false; for (SpecialCycle2.Edge nxt : graph[cur]) { int to = nxt.to; if (to == par || !is_active(cur, to)) continue; if (vis[to]) { low[cur] = Math.min(low[cur], tin[to]); } else { changed |= dfs1(to, cur); low[cur] = Math.min(low[cur], low[to]); if (low[to] == tin[to]) { // this is a bridge if (nxt.is_special) { is_deleted[to] = true; is_deleted[cur] = true; } deleted_edge[to][cur] = deleted_edge[cur][to] = true; changed = true; } } } return changed; } int dfs2(int node) { vis[node] = true; int s = 1; seen.add(node); for (SpecialCycle2.Edge nxt : graph[node]) { if (!is_active(node, nxt.to) || vis[nxt.to]) continue; s += dfs2(nxt.to); } return s; } public Check2(List[] graph, boolean[] is_deleted, boolean[][] deleted_edge) { this.graph = graph; this.n = graph.length; this.is_deleted = Arrays.copyOf(is_deleted, is_deleted.length); this.deleted_edge = new boolean[n][n]; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) this.deleted_edge[i][j] = deleted_edge[i][j]; } public boolean run() { vis = new boolean[n]; low = new int[n]; tin = new int[n]; time = 0; stack = new int[n]; sidx = 0; while (true) { boolean found = false; Arrays.fill(vis, false); Arrays.fill(low, 0); Arrays.fill(tin, 0); Arrays.fill(stack, 0); time = 0; sidx = 0; for (int i = 0; i < n; i++) { if (!vis[i]) { found |= dfs1(i, -10); } } if (!found) break; } // now problem is we have some 2-connected components, no special edges share a node // answer always exists in any component with size > 1 Arrays.fill(vis, false); seen = new HashSet<>(); boolean found = false; for (int i = 0; i < n; i++) { if (!vis[i] && !is_deleted[i]) { seen.clear(); int sz = dfs2(i); if (sz > 1) { // delete all nodes in other components for (int j = 0; j < n; j++) { if (!seen.contains(j)) { is_deleted[j] = true; } } found = true; break; } } } return found; } } } static class DisjointSets { public static int[] createSets(int size) { int[] p = new int[size]; for (int i = 0; i < size; i++) p[i] = i; return p; } } static class OutputWriter { private final PrintWriter writer; public OutputWriter(OutputStream outputStream) { writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream))); } public OutputWriter(Writer writer) { this.writer = new PrintWriter(writer); } public void print(Object... objects) { for (int i = 0; i < objects.length; i++) { if (i != 0) { writer.print(' '); } writer.print(objects[i]); } } public void println() { writer.println(); } public void close() { writer.close(); } public void print(int i) { writer.print(i); } public void println(int i) { writer.println(i); } } static class InputReader2 { private BufferedReader in; private StringTokenizer st; public InputReader2(InputStream stream) { in = new BufferedReader(new InputStreamReader(stream)); st = null; } public int nextInt() { return Integer.parseInt(next()); } public String next() { if (st == null || !st.hasMoreTokens()) { try { st = new StringTokenizer(in.readLine()); } catch (IOException e) { e.printStackTrace(); } } return st.nextToken(); } } static class LUtils { public static List[] genArrayList(int size) { return Stream.generate(ArrayList::new).limit(size).toArray(List[]::new); } } }