import java.io.*; import java.util.*; /* * Problem: Planes, Trains, but not Automobiles (NAIPC19) * Author: Alex Coleman * Complexity: O(sqrt(n)*m) * * First part of the problem (fewest planes) is simply minimum path cover, which can be * solved with flow. For second part, we want to know which nodes can be unmatched in some * maximum matching. If it's unmatched, that corresponds to being at the beginning or end of a leg of the trip. * So as long as we have >= 2 legs in our trip (i.e. >= 1 flight), we can order them s.t. we use this airport. * * Wlog, assume the node (U) is on the right side of BPM (we can flip the graph for left side). * Naively, we can try to push a unit of flow from U to the sink in the residual graph. This will find an 'augmenting' path * which undoes the matching of U and rematches something else. This would be O(V+E) per node though. * Instead, we can reverse the edges of the residual graph and BFS once from the sink (telling us for every node if the sink can reach them) */ public class TrainsPlanesNoAutos_arc { public static void main(String[] args) { Scanner in = new Scanner(System.in); PrintWriter out = new PrintWriter(System.out); int n = in.nextInt(), m = in.nextInt(); Dinic g = new Dinic(n+n); for(int i=0;i= 1 plane, any city that has either no predecessor or no successor can be an airport city if(planes > 0) { // If there is a path from U (rhs version) to sink in residual, then we // can undo U's incoming edge in the matching, and keep the flow the // same (thus allowing U to be used as an arrival airport) // Same for path from source to U (lhs version) in residual allowing a departure airport boolean[] arrivalAirport = bfs(g, g.t, false); boolean[] departureAirport = bfs(g, g.s, true); boolean first = true; for(int i=0;i q = new ArrayDeque<>(); q.offer(s); boolean[] seen = new boolean[g.n]; seen[s] = true; while(!q.isEmpty()) { int u = q.poll(); for(Dinic.Edge e : g.adj[u]) { if(e.flow < e.cap == useResidual && !seen[e.v2]) { seen[e.v2] = true; q.offer(e.v2); } } } return seen; } static class Dinic { static class Edge { int v1, v2, cap, flow; Edge rev; Edge(int V1, int V2, int Cap, int Flow) { v1 = V1; v2 = V2; cap = Cap; flow = Flow; } } ArrayDeque q; ArrayList[] adj; int n, s, t, oo = (int) 1E9; boolean[] blocked; int[] dist; public Dinic(int N) { n = N; s = n++; t = n++; blocked = new boolean[n]; dist = new int[n]; q = new ArrayDeque(); adj = new ArrayList[n]; for (int i = 0; i < n; ++i) adj[i] = new ArrayList(); } void add(int v1, int v2, int cap) { Edge e = new Edge(v1, v2, cap, 0); Edge rev = new Edge(v2, v1, 0, 0); adj[v1].add(rev.rev = e); adj[v2].add(e.rev = rev); } boolean bfs() { q.clear(); Arrays.fill(dist, -1); dist[t] = 0; q.add(t); while (!q.isEmpty()) { int node = q.poll(); if (node == s) return true; for (Edge e : adj[node]) { if (e.rev.cap > e.rev.flow && dist[e.v2] == -1) { dist[e.v2] = dist[node] + 1; q.add(e.v2); } } } return dist[s] != -1; } int dfs(int pos, int min) { if (pos == t) return min; int flow = 0; for (Edge e : adj[pos]) { int cur = 0; if (!blocked[e.v2] && dist[e.v2] == dist[pos] - 1 && e.cap - e.flow > 0) { cur = dfs(e.v2, Math.min(min - flow, e.cap - e.flow)); e.flow += cur; e.rev.flow = -e.flow; flow += cur; } if (flow == min) return flow; } blocked[pos] = flow != min; return flow; } int flow() { int ret = 0; while (bfs()) { Arrays.fill(blocked, false); ret += dfs(s, oo); } return ret; } } }