Reachable Pairs
Explicación
El problema nos pide hallar el número de pares de nodos que son alcanzables entre sí, así que basta con llevar la cuenta del tamaño de cada componente conexa. Si hay componentes conexas y la -ésima componente tiene tamaño , entonces el número de pares alcanzables es
ya que dos nodos son alcanzables si y solo si pertenecen a la misma componente conexa. Para mantener los valores de , podemos usar un Union-Find / conjuntos disjuntos (DSU).
En cada instante , si , entonces se quitan todas las aristas incidentes al nodo , lo que parte la componente del nodo en varias componentes. Si , entonces el nodo se quita de su componente, pero se agregan aristas entre todos los pares de vecinos del nodo , impidiendo que la componente se parta. En lugar de agregar todas estas aristas extra, podemos simplemente “desactivar” el nodo , reduciendo el tamaño de su componente en , mientras mantenemos las aristas adyacentes a él. Esto funciona porque dos vecinos cualesquiera del nodo solo pueden desconectarse cuando uno de ellos se quita.
Como quitar aristas no es posible en un DSU, en su lugar simulamos el proceso al revés, empezando con el DSU del grafo final y agregando aristas y nodos a medida que vamos hacia atrás. Para crear este grafo final, ponemos en los tamaños de las componentes de los nodos donde (todas las demás componentes deberían empezar con tamaño ). Luego, conectamos todas las aristas entre nodos y donde . Ahora, procesamos cada al revés. Si , entonces aumentamos el tamaño de la componente del nodo en . En caso contrario, si , agregamos todas las aristas incidentes al nodo a nuestro DSU.
Para calcular la respuesta, podemos guardar una variable (inicialmente igual a ). Cada vez que se enlazan dos componentes de tamaños e , sumamos a (ya que cada par de nodos con un nodo de cada una de las dos componentes ahora es alcanzable). Cada vez que el tamaño de una componente cambia de a , sumamos a (ya que el nodo agregado crea pares alcanzables, uno con cada otro nodo de su componente).
Implementación
Complejidad temporal:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct DSU {
// s es el tamaño de la componente, p es el padre
vector<int> p, s;
DSU(int n) : p(n, -1), s(n, 1) {}
int parent(int x) {
if (p[x] == -1) return x;
else return p[x] = parent(p[x]);
}
int &size(int x) { return s[parent(x)]; }
bool link(int x, int y) {
x = parent(x), y = parent(y);
if (x == y) return false;
if (size(x) < size(y)) swap(x, y);
s[x] += s[y];
p[y] = x;
return true;
}
};
ll xchoose2(ll x) { return x * (x - 1) / 2; }
void solve() {
int n, m;
string s;
cin >> n >> m >> s;
vector<vector<int>> adj(n);
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
a--;
b--;
adj[a].push_back(b);
adj[b].push_back(a);
}
DSU dsu(n);
for (int i = 0; i < n; i++) {
if (s[i] == '1') {
dsu.size(i) = 0;
for (int j : adj[i]) {
if (s[j] == '1') { dsu.link(i, j); }
}
}
}
ll pairs = 0;
vector<ll> ans;
for (int i = n - 1; i >= 0; i--) {
if (s[i] == '1') {
pairs += dsu.size(i);
dsu.size(i)++;
} else {
for (int j : adj[i]) {
if (j < i && s[j] == '0') continue;
if (dsu.parent(i) == dsu.parent(j)) continue;
pairs += (ll)dsu.size(i) * dsu.size(j);
dsu.link(i, j);
}
}
ans.push_back(pairs);
}
reverse(ans.begin(), ans.end());
for (auto i : ans) cout << i << '\n';
}
signed main() {
cin.tie(0)->sync_with_stdio(0);
solve();
}