Skip to Content

Hard Route

Explicación

Supongamos que hay alguna ruta difícil que va del vértice uu al vértice vv. Sea xx el nodo en el que se encuentran el camino de uu a vv y el nodo más lejano de la ruta difícil. Usamos DP de rerooting para calcular la ruta más difícil y la cantidad de tales rutas más difíciles para cada nodo xx.

Implementación

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

#include <bits/stdc++.h> using namespace std; using ll = long long; int main() { int n; cin >> n; vector<vector<int>> adj(n); for (int i = 1; i < n; i++) { int x, y; cin >> x >> y; adj[--x].push_back(--y); adj[y].push_back(x); } vector<int> max_length(n); vector<int> path_count(n); /** * Calcula el camino más largo desde el vértice u, * y la cantidad de tales caminos. */ function<void(int, int)> dfs = [&](int u, int p) { max_length[u] = 0; path_count[u] = 1; for (int v : adj[u]) if (v != p) { dfs(v, u); if (max_length[u] < max_length[v] + 1) { max_length[u] = max_length[v] + 1; path_count[u] = path_count[v]; } else if (max_length[v] + 1 == max_length[u]) { path_count[u] += path_count[v]; } } }; dfs(0, -1); ll max_hardness = 0; ll hardest_path_count = 1; /** * Realiza el rerooting, para contar el camino más difícil * y la cantidad de tales caminos en este vértice. */ function<void(int, int, ll, ll)> dfs2 = [&](int u, int p, ll parent_dist, ll parent_count) { vector<array<ll, 2>> paths; // {distancia, cantidad} if (u > 0 || (int)adj[u].size() == 1) { paths.push_back({parent_dist, parent_count}); } for (int v : adj[u]) if (v != p) { paths.push_back({max_length[v] + 1, path_count[v]}); } sort(paths.begin(), paths.end(), greater<>()); if ((int)adj[u].size() >= 3) { // puede formar una ruta de dureza no nula /* * Sean a, b, c las 3 longitudes de camino más largas, con a > b > c. * La "dureza" óptima de la ruta difícil es a * (b + c). */ ll a = paths[0][0]; ll b = paths[1][0]; ll c = paths[2][0]; ll current_hardness = a * (b + c); ll current_path_count = 0; ll ties = 0; for (auto [len, amt] : paths) { if (len == c) { ties += amt; } } if (a != b && b != c) { // caso 1: todas son distintas. current_path_count = paths[1][1] * ties; } else if (a == b && b == c) { // caso 2: todas son iguales. current_path_count = ties * ties; for (auto [len, amt] : paths) { if (len == a) { current_path_count -= amt * amt; } } current_path_count /= 2; // evitamos el doble conteo } else if (a == b) { // caso 3: las dos primeras son iguales. current_path_count = (paths[0][1] + paths[1][1]) * ties; } else { // caso 4: las dos últimas son iguales. current_path_count = ties * ties; for (auto [len, amt] : paths) { if (len == c) { current_path_count -= amt * amt; } } current_path_count /= 2; // evitamos el doble conteo } if (max_hardness < current_hardness) { max_hardness = current_hardness; hardest_path_count = current_path_count; } else if (max_hardness == current_hardness) { hardest_path_count += current_path_count; } } // procesamos la distancia y la cantidad del padre. ll longest1 = 0; ll longest2 = 0; ll count1 = 0; ll count2 = 0; for (auto [len, amt] : paths) { if (len + 1 > longest1) { swap(longest1, longest2); swap(count1, count2); longest1 = len + 1; count1 = amt; } else if (len + 1 == longest1) { count1 += amt; } else if (len + 1 > longest2) { longest2 = len + 1; count2 = amt; } else if (len + 1 == longest2) { count2 += amt; } } for (int v : adj[u]) if (v != p) { // usamos la mejor dureza y cantidad del padre posibles. if (max_length[v] + 2 == longest1) { (path_count[v] == count1) ? dfs2(v, u, longest2, count2) : dfs2(v, u, longest1, count1 - path_count[v]); } else { dfs2(v, u, longest1, count1); } } }; dfs2(0, -1, 0, 1); cout << max_hardness << ' ' << hardest_path_count << '\n'; }