Skip to Content

Visits

Análisis oficial (C++) 

Explicación

Como cada vaca ii solo quiere visitar a una de las otras vacas jj, podemos interpretar la entrada como un grafo funcional  con aristas iji \to j. El grafo resultante no es necesariamente conexo, así que hay que hallar todas las componentes conexas de este grafo y encontrar la cantidad máxima de “moos” de cada una por separado.

Para cada componente conexa del grafo funcional, observemos que debe haber uno y solo un ciclo. Además, todas las vacas pueden visitar a sus compañeras antes de irse excepto una, que quiere visitar a la vaca que inicia la cadena de visitas del ciclo. Para los demás caminos que llevan al ciclo, siempre podemos visitarlos primero antes de procesar el ciclo, así que siempre podrán hacer “moo”. Por tanto, la cantidad máxima de “moos” es la suma de todos los vv de la componente conexa. Hay que restar de la suma el viv_i mínimo del ciclo porque representa a la vaca anterior a la que inicia, que no podrá hacer moo.

Para cada vaca, primero comprobamos si ya está visitada. Si no, queremos hallar todas las demás vacas de esta componente conexa usando un grafo invertido y sumando los “moos”. Después, ejecutamos el algoritmo de Floyd para determinar el ciclo y hallar el valor mínimo de “v” en él.

Implementación

Complejidad temporal: O(N)\mathcal{O}(N)

#include <bits/stdc++.h> using namespace std; // la vaca i quiere visitar a la vaca a[i] y obtiene v[i] puntos vector<int> a, v; // reversed_graph[i] guarda las vacas que quieren ir a la granja i vector<vector<int>> reversed_graph; // marca las vacas como visitadas una vez que las hemos procesado vector<bool> visited; /** * Marca y y las demás vacas de su otro ciclo como visitadas haciendo una dfs. */ void mark(int y) { if (visited[y]) { return; } visited[y] = true; for (int c : reversed_graph[y]) { mark(c); } } /** * Aplica el algoritmo de Floyd para detectar el ciclo y devolver el valor * mínimo de v en este ciclo. */ int min_in_cycle(int curr) { int y = a[curr]; int z = a[y]; while (y != z) { y = a[y]; z = a[a[z]]; } // y es ahora un elemento que está en el ciclo int min_v = v[y]; y = a[y]; // recorrer el ciclo para hallar la vaca con el valor mínimo de v_i while (y != z) { min_v = min(min_v, v[y]); y = a[y]; } // marcar todos los nodos de esta componente conexa como visitados mark(y); return min_v; } int main() { int n; cin >> n; v.resize(n); a.resize(n); visited.resize(n); reversed_graph.resize(n); long long max_moos = 0; for (int i = 0; i < n; i++) { cin >> a[i] >> v[i]; a[i]--; // la vaca i quiere visitar la granja a[i] reversed_graph[a[i]].push_back(i); max_moos += v[i]; } for (int i = 0; i < n; i++) { /* * Para cada componente conexa no visitada con exactamente un ciclo, se * puede visitar a todas excepto una vaca. Hacemos que esta vaca sea la * de menor v_i y la restamos. */ if (!visited[i]) { max_moos -= min_in_cycle(i); } } cout << max_moos << endl; }
import java.io.*; import java.util.*; public class Visits { // la vaca i quiere visitar a la vaca a[i] y obtiene v[i] puntos static List<Integer> a, v; // reversed_graph[i] guarda las vacas que quieren ir a la granja i static List<List<Integer>> reversed_graph; // marca las vacas como visitadas una vez que las hemos procesado static List<Boolean> visited; public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine()); a = new ArrayList<>(n); v = new ArrayList<>(n); reversed_graph = new ArrayList<>(n); for (int i = 0; i < n; i++) { reversed_graph.add(new ArrayList<>()); } visited = new ArrayList<>(); long maxMoos = 0; for (int i = 0; i < n; i++) { StringTokenizer st = new StringTokenizer(br.readLine()); a.add(Integer.parseInt(st.nextToken()) - 1); v.add(Integer.parseInt(st.nextToken())); // la vaca i quiere visitar la granja a[i] reversed_graph.get(a.get(i)).add(i); visited.add(false); maxMoos += v.get(i); } for (int i = 0; i < n; i++) { /* * Para cada componente conexa no visitada con exactamente un ciclo, * se puede visitar a todas excepto una vaca. Hacemos que esta vaca * sea la de menor v_i y la restamos. */ if (!visited.get(i)) { maxMoos -= minInCycle(i); } } System.out.println(maxMoos); } /** * Aplica el algoritmo de Floyd para detectar el ciclo y devolver el valor * mínimo de v en este ciclo que contiene al nodo. */ static int minInCycle(int node) { int slow = a.get(node); int quick = a.get(slow); while (slow != quick) { slow = a.get(slow); quick = a.get(a.get(quick)); } // slow es ahora un elemento que está en el ciclo int min_v = v.get(slow); // recorrer el ciclo para hallar la vaca con el valor mínimo de v_i slow = a.get(slow); while (slow != quick) { min_v = Math.min(min_v, v.get(slow)); slow = a.get(slow); } // marcar todos los nodos de esta componente conexa como visitados mark(slow); return min_v; } /** * Marca y y las demás vacas de su otro ciclo como visitadas haciendo una * dfs. */ static void mark(int node) { if (visited.get(node)) { return; } visited.set(node, true); for (Integer child : reversed_graph.get(node)) { mark(child); } } }