Taskauthor
Parte 1 - algoritmos de camino más corto
En este subproblema, nos piden crear casos de prueba que rompan ciertos algoritmos de camino más corto provocando TLE.
Esto es muy educativo porque obliga a analizar los cuellos de botella de estos algoritmos y cómo funcionan exactamente.
Subtareas 1 y 3 - romper Floyd-Warshall
El algoritmo de Floyd-Warshall siempre usa exactamente iteraciones.
Esto significa que podemos simplemente poner en 101 y en 0 para forzar TLE.
Código
print(101) # Generar el grafo
for i in range(101):
print(0)
print(1) # Una sola consulta
print(0, 1)Subtareas 2 y 5 - romper Bellman-Ford
El algoritmo de Bellman-Ford normalmente usa exactamente iteraciones, pero en este caso está un poco optimizado.
Aun así, podemos forzar al algoritmo a usar exactamente iteraciones.
Primero, observemos que debemos tener o si no el algoritmo cortará antes de tiempo.
Si miramos la implementación de este Bellman-Ford en particular, notemos que “relajamos” las aristas conectadas al nodo 1, luego al nodo 2, etc.
Esto significa que si tenemos una línea recta desde el nodo hasta el nodo 0 (y no hay otras aristas), entonces solo se relajará 1 arista por ciclo.
También podemos agregar algunas auto-aristas (con pesos positivos) del nodo 0 a sí mismo para aumentar sin disparar un corte temprano, ya que nunca es óptimo ir de un nodo a sí mismo vía una auto-arista.
para la subtarea 2 y para la subtarea 5 nos permiten forzar TLE.
Código
print(300) # Cambiar (300, 48) por (100, 1011) para la subtarea 2
print(48, end=" ")
for i in range(48):
print(0, 1, end=" ") # Auto-aristas
print()
for i in range(1, 300):
print(1, i - 1, 1)
print(10) # 10 consultas
for i in range(10):
print(299, 0)Subtareas 4 y 6 - romper Dijkstra
A primera vista, estas subtareas parecen imposibles: ¡el algoritmo de Dijkstra siempre tiene complejidad !
… ¿o no?
Observemos que la complejidad solo es cierta si no hay aristas negativas.
¿Qué ocurre cuando hay aristas negativas en el grafo? Consideremos la siguiente construcción:

Si no hubiera aristas negativas, solo iteraríamos las aristas conectadas al nodo más a la derecha una vez.
Sin embargo, notemos que en la construcción de arriba, iteraremos esas aristas dos veces.
Esto significa que si encadenamos de esos triángulos, el algoritmo de Dijkstra usará iteraciones — mucho más que el esperado.
Con 16 triángulos, podemos forzar TLE.
Código
print(33)
print(0)
print(1, 0, 1)
print(1, 1, 1)
for i in range(2, 31, 2):
print(1, i, -2 * (2 ** (i // 2)))
print(2, i + 1, 2 ** (i // 2), i, 0)
print(7)
for i in range(7):
print(32, 0)Parte 2 - el problema “misterioso”
Subtarea 7 - forzar TLE
Código
print(250, 1501)
for i in range(300):
for j in range(i + 1, 250, i + 1):
print(i, j)
for i in range(88):
print(1, 2 * i + 3)Subtarea 8 - forzar que no haya TLE
Código
print(753, 1501)
for i in range(1, 752):
if i != 1:
print(0, i)
print(i, 752)Solución: subtareas 1-6
int main() {
FOR(TC, 1, 7) {
setOut("apio-2013-taskauthor_" + ts(TC) + ".out");
int V;
vpi adj[300];
vpi query;
if (TC == 1 || TC == 3) {
V = 101;
query.pb({0, 0});
} else if (TC == 2) {
V = 100;
F0R(i, V - 1) adj[i + 1].pb({i, -1});
F0R(i, 950) adj[0].pb({1, 1000});
F0R(Q, 10) query.pb({V - 1, 0});
} else if (TC == 5) {
V = 300;
F0R(i, V - 1) adj[i + 1].pb({i, -1});
F0R(i, 40) adj[0].pb({1, 1000});
F0R(Q, 10) query.pb({V - 1, 0});
} else if (TC == 4 || TC == 6) {
V = 33; // 1+V+(V/2)*6+1+6*2
int wei = 999999;
for (int i = 0; i < V - 1; i += 2) {
adj[i].pb({i + 2, -1});
adj[i].pb({i + 1, 0});
adj[i + 1].pb({i + 2, -wei});
wei /= 2;
}
F0R(i, 6) query.pb({0, V - 1});
}
ps(V);
F0R(i, V) {
pr(sz(adj[i]));
trav(t, adj[i]) pr(' ', t.f, ' ', t.s);
ps();
}
ps(sz(query));
trav(t, query) ps(t.f, t.s);
}
}