Fusión small-to-large
| Fuente | Recurso | Notas |
|---|---|---|
| CPH | 18.4 - Merging Data Structures | |
| CF | Arpa - Sack (DSU on Tree) | |
| CF | tuwuna - Explaining DSU on Trees |
Fusionar estructuras de datos
Es evidente que las listas enlazadas se pueden fusionar en tiempo . Pero ¿qué hay de los conjuntos o los vectores?
| Hecho | Fuente | Nombre | Dificultad | Tags | Solución |
|---|---|---|---|---|---|
| CSES | Distinct Colors | Fácil | Merging | Solución |
Consideremos un árbol enraizado en el nodo , donde cada nodo tiene un color.
Para cada nodo, almacenemos un conjunto que contiene solo ese nodo, y queremos fusionar los conjuntos en el subárbol del nodo de modo que cada nodo tenga un conjunto formado por todos los colores del subárbol del nodo. Hacer esto nos permite resolver una variedad de problemas, como consultar la cantidad de colores distintos en cada subárbol.
Solución naive
Supongamos que queremos fusionar dos conjuntos y de tamaños y , respectivamente. Una posibilidad es la siguiente:
for (int x : b) a.insert(x);que corre en tiempo , lo que da un tiempo de ejecución de en el peor caso. Si en cambio mantenemos y como vectores ordenados, podemos fusionarlos en tiempo , pero también es demasiado lento.
Mejor solución
Con solo una línea extra de código, podemos acelerar esto de forma significativa.
if (a.size() < b.size()) swap(a, b);
for (int x : b) a.insert(x);Observar que swap intercambia dos conjuntos en tiempo . Así, fusionar un conjunto más pequeño de tamaño en el más grande de tamaño toma tiempo .
Afirmación: La solución corre en tiempo .
Demostración: Al fusionar dos conjuntos, se mueve del conjunto más pequeño al más grande. Si el tamaño del conjunto más pequeño es , entonces el tamaño del conjunto resultante es al menos . Así, un elemento que se ha movido veces estará en un conjunto de tamaño al menos , y como el tamaño máximo de un conjunto es (la raíz), cada elemento se moverá a lo sumo ) veces.
Código completo
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 2e5;
// nodes will be 1-indexed like in the problem
vector<int> adj[MAX_N + 1];
set<int> colors[MAX_N + 1];
int distinct_num[MAX_N + 1];
void process_colors(int curr, int parent) {
for (int n : adj[curr]) {
if (n != parent) {
process_colors(n, curr);
// make x the larger set always
if (colors[curr].size() < colors[n].size()) {
swap(colors[curr], colors[n]);
}
for (int item : colors[n]) { colors[curr].insert(item); }
}
}
distinct_num[curr] = colors[curr].size();
}
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
int a;
cin >> a;
colors[i].insert(a);
}
for (int i = 1; i < n; i++) {
int a;
int b;
cin >> a >> b;
adj[a].push_back(b);
adj[b].push_back(a);
}
process_colors(1, 0);
for (int i = 1; i <= n; i++) { cout << distinct_num[i] << (i < n ? " " : "\n"); }
}Generalización
También podemos fusionar otras estructuras de datos de la librería estándar
como std::map o std:unordered_map de la misma forma. Sin embargo,
std::swap no siempre
corre en tiempo . Por ejemplo, intercambiar
std::arrays toma
tiempo lineal en la suma de los tamaños de los arreglos, y lo mismo ocurre con
las estructuras de datos policy-based de GCC
como __gnu_pbds::tree o __gnu_pbds::gp_hash_table.
Para intercambiar dos estructuras de datos policy-based a y b en tiempo
, usar a.swap(b) en su lugar. Observar que para las
estructuras de datos de la librería estándar, swap(a,b) es equivalente a
a.swap(b).
Problemas
| Hecho | Fuente | Nombre | Dificultad | Tags | Solución |
|---|---|---|---|---|---|
| CF | Lomsat gelral | Normal | Merging | Solución | |
| Platinum | Promotion Counting | Normal | Merging, Indexed Set | Solución | |
| Platinum | Disruption | Normal | Merging | Solución | |
| POI | 2011 - Tree Rotations | Normal | Merging, Indexed Set | Solución | |
| IOI | ★ 2011 - Race | Normal | Centroid, Merging | Solución | |
| JOI | 2020 - Joitter | Difícil | Merging | Solución | |
| COI | 2009 - Loza | Difícil | Merging | — | |
| JOI | 2019 - Virus | Muy difícil | Merging, SCC | — |
Fusión más rápida
Es fácil fusionar dos conjuntos de tamaños en tiempo o , pero a veces puede ser significativamente mejor que ambos. Ver “Advanced - Treaps” para más detalles. También ver este enlace sobre fusionar árboles de segmentos.