Skip to Content

Duff in the Army

Análisis oficial 

Explicación

Nótese que para cada par de nodos uu y vv, el camino entre ellos es simplemente uu a lca(u,v)lca(u,v) y vv a lca(u,v)lca(u,v). Nótese también la restricción de a10a \leq 10 para cada consulta. Para responder cada consulta, solo necesitamos guardar las 1010 personas con los ids más pequeños para el camino de uu a lca(u,v)lca(u,v) y de vv a lca(u,v)lca(u,v), y combinarlas. Esto se puede hacer con binary lifting, que guardará los 1010 ids más pequeños de cada vértice a su 2k2^k-ésimo ancestro.

Implementación

Podemos responder cada consulta en O(alogn)\mathcal{O}(a \log n). Aunque el factor constante es bastante malo, los límites del problema dan margen para ello. ¡Nótese que si se guardan más de 1010 ids más pequeños el problema dará MLE!

Complejidad temporal: O(alogn)\mathcal{O}(a \log n) para cada consulta.

#include <bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 1; const int MAXL = 20; // approximately maximum log // for euler-tour array<int, MAXN> enter_time, exit_time, depth; int timer = 1; // for binary lifting array<array<int, MAXL>, MAXN> up; // up[i][j] stores the 2^jth ancestor for i // stores the (10) minimum people from the path from i to 2^jth ancestor vector<int> minimum_up[MAXN][MAXL]; vector<int> graph[MAXN]; // main adjacency list vector<int> living_at[MAXN]; // number of people living at each city void remove_extra(vector<int> &x) { // keeps only the first 10 elements (note constraint on a) while (x.size() > 10) { x.pop_back(); } } // combine two vectors and keep the 10 minimum among them void combine(vector<int> &a, vector<int> b) { // combine vector b into vector a a.insert(a.end(), b.begin(), b.end()); sort(a.begin(), a.end()); remove_extra(a); } void dfs(int node, int parent) { // initialize with direct parent information depth[node] = depth[parent] + 1; up[node][0] = parent; combine(minimum_up[node][0], living_at[parent]); enter_time[node] = timer++; for (int child : graph[node]) { if (child != parent) { dfs(child, node); } } exit_time[node] = timer - 1; } // checks if node a is an ancestor of b using euler tour bool is_ancestor(int a, int b) { return enter_time[a] <= enter_time[b] && exit_time[a] >= exit_time[b]; } int lca(int a, int b) { if (is_ancestor(a, b)) { return a; } for (int i = MAXL - 1; i >= 0; i--) { if (!is_ancestor(up[a][i], b)) { a = up[a][i]; } } return up[a][0]; } // gather all people going from a node to its k'th ancestor and // store the minimums in the people array void trace_path(int node, int k, vector<int> &people) { for (int i = 0; i < MAXL; i++) { if (k & (1 << i)) { combine(people, minimum_up[node][i]); node = up[node][i]; } } } int main() { int n, m, q; scanf("%d %d %d", &n, &m, &q); for (int i = 0; i < n - 1; i++) { int u, v; scanf("%d %d", &u, &v); graph[u].push_back(v); graph[v].push_back(u); } for (int i = 1; i <= m; i++) { int city; scanf("%d", &city); living_at[city].push_back(i); } // sort from smallest to largest and keep the 10 minimums for (int i = 1; i <= n; i++) { sort(living_at[i].begin(), living_at[i].end()); remove_extra(living_at[i]); } dfs(1, 1); // fill in information for binary lifting for (int k = 1; k < MAXL; k++) { for (int i = 1; i <= n; i++) { up[i][k] = up[up[i][k - 1]][k - 1]; // combine people from the left and the right combine(minimum_up[i][k], minimum_up[i][k - 1]); combine(minimum_up[i][k], minimum_up[up[i][k - 1]][k - 1]); } } for (int query = 0; query < q; query++) { int u, v, a; scanf("%d %d %d", &u, &v, &a); vector<int> min_people; int least_common_ancestor = lca(u, v); // gather the people living at cities u and v if (least_common_ancestor != u) { combine(min_people, living_at[u]); } if (least_common_ancestor != v) { combine(min_people, living_at[v]); } // gather people living in path from lca to u and lca to v trace_path(u, max(0, depth[u] - depth[least_common_ancestor] - 1), min_people); trace_path(v, max(0, depth[v] - depth[least_common_ancestor] - 1), min_people); combine(min_people, living_at[least_common_ancestor]); int k = min((int)(min_people.size()), a); printf("%d ", k); for (int i = 0; i < k; i++) { printf("%d ", min_people[i]); } puts(" "); } }