2009 - Regions
Pista #1
Consideremos precalcular todas las respuestas a consultas que involucran alguna región padre .
Pista #2
Podemos responder todas las consultas en , donde es el tamaño de la región padre . ¿Cómo deberíamos elegir las regiones que precalculamos, dada esta información?
Respuesta a la Pista #2
Precalculamos la respuesta para todas las consultas que involucran regiones de tamaño mayor que .
Explicación
Como se mencionó en las pistas, precalcularemos la respuesta para todas las regiones padre de tamaño mayor que . Luego, respondemos las consultas o bien calculando la respuesta online o bien usando una respuesta precalculada.
Para precalcular todas las respuestas de una región padre dada, hacemos DFS sobre el árbol. Mientras recorremos el árbol hacia abajo, llevamos la cuenta de cuántas veces hemos visto un empleado que forma parte de esta región padre actual. Este número lleva la cuenta de las ocurrencias de la región padre como supervisor de la región de nuestro empleado actual. Con esta información, podemos guardar estas respuestas en un arreglo , donde es nuestra región padre y es cualquier otra región. Notemos que, como solo precalculamos a lo sumo valores de , la complejidad espacial es .
Para regiones padre de tamaño menor que , respondemos las consultas online. Antes de responder consultas, ejecutamos un tour de Euler del árbol. Para cada nodo dentro de esta región padre actual, contamos la cantidad de nodos en la región hija. Como el subárbol de cada nodo contiene un rango de nodos (cuando se etiquetan por sus tiempos de recorrido del tour de Euler), podemos usar búsqueda binaria para contar la cantidad de tales nodos hijos.
Implementación
Complejidad temporal:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, r, q;
cin >> n >> r >> q;
vector<int> region(n);
cin >> region[0];
region[0]--;
vector<vector<int>> adj(n);
for (int i = 1; i < n; i++) {
int p, h;
cin >> p >> h;
p--, h--;
adj[p].push_back(i);
region[i] = h;
}
// Calculamos los índices del tour de Euler.
vector<int> tin(n);
vector<int> tout(n);
vector<int> tin_node(n); // tin[tin_node[i]] = i
vector<vector<int>> comp(r);
int timer = 0;
function<void(int)> tour = [&](int u) -> void {
tin[u] = timer++;
tin_node[tin[u]] = u;
comp[region[u]].push_back(tin[u]);
for (int v : adj[u]) { tour(v); }
tout[u] = timer;
};
tour(0);
// Precalculamos la respuesta para regiones padre
// con >= sqrt(n) miembros.
const int BLOCK = sqrt(n);
vector<vector<int>> calc;
vector<int> region_id(r, -1);
function<void(int, int, int)> dfs = [&](int u, int parent_region,
int parent_count) -> void {
if (region[u] == parent_region) { parent_count++; }
calc[region_id[parent_region]][region[u]] += parent_count;
for (int v : adj[u]) { dfs(v, parent_region, parent_count); }
};
int current_id = 0;
for (int i = 0; i < r; i++) {
if ((int)comp[i].size() >= BLOCK) {
region_id[i] = current_id++;
calc.push_back(vector<int>(r));
dfs(0, i, 0);
}
}
// Respondemos las consultas, o bien con los valores precalculados
// o bien usando tour de Euler + búsqueda binaria.
for (int i = 0; i < q; i++) {
int e1, e2;
cin >> e1 >> e2;
e1--, e2--;
if ((int)comp[e1].size() >= BLOCK) {
cout << calc[region_id[e1]][e2] << "\n";
} else {
int total = 0;
for (int u : comp[e1]) {
total +=
lower_bound(begin(comp[e2]), end(comp[e2]), tout[tin_node[u]]) -
lower_bound(begin(comp[e2]), end(comp[e2]), tin[tin_node[u]]);
}
cout << total << '\n';
}
}
}