Ski Course Rating
Explicación
Primero, notemos que una vaca puede moverse a lo largo de las aristas cuya diferencia de elevación es a lo sumo . Así, las celdas alcanzables desde un punto de partida son las celdas de su componente conexa en este grafo.
Luego, ordenamos todas las aristas por diferencia de elevación e iteramos sobre ellas en orden creciente, de forma similar al algoritmo de Kruskal, usando un DSU para mantener las componentes conexas. A medida que agregamos aristas, efectivamente aumentamos el permitido.
También mantenemos, para cada componente del DSU, la cantidad de puntos de partida en esa componente, de forma similar a cómo mantenemos los tamaños de las componentes del DSU.
Cuando una componente alcanza por primera vez un tamaño de al menos , el peso de la arista actual es exactamente la dificultad mínima requerida para que esa componente tenga tamaño . Por lo tanto, todos los puntos de partida en ella ya encontraron su dificultad mínima requerida, así que sumamos su contribución (cantidad de puntos de partida en la componente multiplicada por esta dificultad mínima) a la respuesta total y reiniciamos su conteo a para no contar dos veces.
Implementación
Complejidad temporal:
import sys
sys.stdin = open("skilevel.in")
sys.stdout = open("skilevel.out", "w+")
# BeginCodeSnip{Disjoint Set Union}
class DisjointSets:
def __init__(self, size: int) -> None:
self.parents = [i for i in range(size)]
self.sizes = [1 for _ in range(size)]
def find(self, x: int) -> int:
""":return: el nodo "representante" de la componente de x"""
if self.parents[x] == x:
return x
self.parents[x] = self.find(self.parents[x])
return self.parents[x]
def unite(self, x: int, y: int) -> bool:
""":return: si la fusión cambió la conectividad"""
x_root = self.find(x)
y_root = self.find(y)
if x_root == y_root:
return False
if self.sizes[x_root] < self.sizes[y_root]:
x_root, y_root = y_root, x_root
self.parents[y_root] = x_root
self.sizes[x_root] += self.sizes[y_root]
return True
def connected(self, x: int, y: int) -> bool:
""":return: si x e y están en la misma componente conexa"""
return self.find(x) == self.find(y)
# EndCodeSnip
n, m, t = map(int, input().split())
grid = [list(map(int, input().split())) for _ in range(n)]
waypoint_graph = [list(map(int, input().split())) for _ in range(n)]
waypoints = [i * m + j for i in range(n) for j in range(m) if waypoint_graph[i][j] == 1]
counts = [0] * (n * m) # almacena la cantidad de puntos de partida para cada raíz del DSU
dsu = DisjointSets(n * m)
for i in waypoints:
counts[i] = 1
edges = []
moves = [(0, 1), (1, 0)]
# crear aristas con pesos equivalentes a la diferencia de elevación
for i in range(n):
for j in range(m):
for x, y in moves:
if 0 <= i + x < n and 0 <= j + y < m:
edges.append(
(
i * m + j,
(i + x) * m + (j + y),
abs(grid[i + x][j + y] - grid[i][j]), # peso de la arista
)
)
# ordenar las aristas en pesos crecientes para Kruskal
edges.sort(key=lambda x: x[2])
total = 0
for a, b, d in edges:
root_a = dsu.find(a)
root_b = dsu.find(b)
if root_a != root_b:
dsu.unite(a, b)
root = dsu.find(a)
counts[root] = counts[root_a] + counts[root_b]
if dsu.sizes[root] >= t:
total += counts[root] * d
counts[root] = 0 # esta componente ya fue respondida
print(total)Curiosamente, el siguiente código pasa todos los casos de prueba pero no pasaría si ; habría que hacer un caso especial para eso.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct Edge {
int u, v, w;
};
struct DSU {
vector<int> p, sz, unprocessed_starts;
DSU(int n) : p(n), sz(n, 1), unprocessed_starts(n) { iota(p.begin(), p.end(), 0); }
int get(int x) {
if (p[x] != x) { p[x] = get(p[x]); }
return p[x];
}
void unite(Edge e) {
int a = get(e.u);
int b = get(e.v);
if (sz[a] < sz[b]) { swap(a, b); }
if (a != b) {
p[b] = a;
sz[a] += sz[b];
unprocessed_starts[a] += unprocessed_starts[b];
}
}
};
int main() {
ifstream read("skilevel.in");
int n, m, t;
read >> n >> m >> t;
vector<vector<int>> grid(n, vector<int>(m));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) { read >> grid[i][j]; }
}
vector<vector<int>> is_start(n, vector<int>(m));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) { read >> is_start[i][j]; }
}
auto to_int = [&](int r, int c) -> int { return r * m + c; };
vector<Edge> edges;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// agregar una arista entre posiciones adyacentes
if (i < n - 1) {
edges.push_back(
{to_int(i, j), to_int(i + 1, j), abs(grid[i][j] - grid[i + 1][j])});
}
if (j < m - 1) {
edges.push_back(
{to_int(i, j), to_int(i, j + 1), abs(grid[i][j] - grid[i][j + 1])});
}
}
}
// ordenar las aristas por peso
sort(edges.begin(), edges.end(), [&](Edge a, Edge b) { return a.w < b.w; });
DSU dsu(n * m);
// inicializar en el dsu si este es un punto de partida
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (is_start[i][j]) { dsu.unprocessed_starts[to_int(i, j)] = 1; }
}
}
ll ans = 0;
for (Edge e : edges) {
dsu.unite(e);
int par = dsu.get(e.u);
if (dsu.sz[par] >= t) {
// sumar la cantidad de nodos de partida que no procesamos * peso actual
ans += (ll)e.w * dsu.unprocessed_starts[par];
// reiniciar los nodos de partida no procesados a 0
dsu.unprocessed_starts[par] = 0;
}
}
ofstream("skilevel.out") << ans << endl;
}