Skip to Content

War

Explicación

Como la relación de amigos es transitiva, podemos mantener conjuntos en los que todas las personas de un conjunto son amigas. Para cada uno de estos conjuntos de amigos, también habrá un conjunto de enemigos formado por la unión de todos los enemigos de cada persona del conjunto de amigos. Cada persona de este conjunto de enemigos será enemiga de cada persona del conjunto de amigos. Para ver por qué esto es cierto, consideremos una persona aa del conjunto de amigos FF. Su enemigo es bb del conjunto de enemigos EE. Como el enemigo de un amigo también es un enemigo, bb será enemigo de todos los amigos de aa, que son las demás personas de FF. Además, cada persona de EE es amiga de todas las demás personas de EE, ya que comparten un enemigo común aa.

Para representar esto, los conjuntos 0n10 \dots n - 1 en el código serán conjuntos de amigos, mientras que cada conjunto ii en los conjuntos n2n1n \dots 2n - 1 será el de los enemigos del conjunto ini - n.

Para cada consulta, hacemos que las dos personas sean xx e yy y que la raíz de los amigos de xx, los amigos de yy, los enemigos de xx y los enemigos de yy sean xroot\texttt{xroot}, yroot\texttt{yroot}, exroot\texttt{exroot} y eyroot\texttt{eyroot}, respectivamente.

areEnemies

Si alguno de los amigos de xx es amigo de alguno de los enemigos de yy, entonces xx será amigo de uno de los enemigos de yy. Así, xx e yy serán enemigos. Para comprobar si este es el caso, verificamos si xroot\texttt{xroot} es el mismo que eyroot\texttt{eyroot} (lo que significa que los amigos de xx son los enemigos de yy), o si yroot\texttt{yroot} es el mismo que exroot\texttt{exroot} (lo que significa que los amigos de yy son los enemigos de xx).

areFriends

Si xx e yy están en el mismo conjunto, entonces son amigos.

setEnemies

Al marcar a dos personas como enemigas, necesitamos comprobar si son amigas. Podemos usar la función areFriends\texttt{areFriends} para esto. Si no son amigas, unimos el conjunto de xroot\texttt{xroot} con el conjunto de eyroot\texttt{eyroot} y unimos el conjunto de yroot\texttt{yroot} con el conjunto de exroot\texttt{exroot}, ya que el amigo de un enemigo también es un enemigo.

setFriends

Si queremos marcar a dos personas como amigas, primero necesitamos comprobar si son enemigas, usando la función areEnemies\texttt{areEnemies}. Si no son enemigas, podemos unir los conjuntos de xroot\texttt{xroot} y yroot\texttt{yroot}. Los enemigos de dos conjuntos de amigos también se volverán amigos, así que también unimos los conjuntos de exroot\texttt{exroot} y eyroot\texttt{eyroot}.

Implementación

Complejidad temporal: O(Qα(n))\mathcal{O}(Q \cdot \alpha(n)), donde QQ es la cantidad de operaciones

#include <algorithm> #include <iostream> using namespace std; int size[20000], parent[20000]; void init(int n) { for (int i = 0; i < 2 * n; i++) { parent[i] = i; size[i] = 1; } } int find(int a) { if (a == parent[a]) { return a; } return parent[a] = find(parent[a]); } void uniteRoot(int a, int b) { if (size[a] > size[b]) { swap(a, b); } size[b] += size[a]; parent[a] = b; } bool areFriends(int xroot, int yroot) { return xroot == yroot; } bool areEnemies(int xroot, int yroot, int exroot, int eyroot) { // x es amigo del enemigo de y // o y es amigo del enemigo de x return xroot == eyroot || yroot == exroot; } void setFriends(int xroot, int yroot, int exroot, int eyroot) { if (areEnemies(xroot, yroot, exroot, eyroot)) { cout << -1 << endl; return; }; // si x e y son amigos, entonces // los enemigos de x y los enemigos de y también son amigos uniteRoot(exroot, eyroot); uniteRoot(xroot, yroot); } void setEnemies(int xroot, int yroot, int exroot, int eyroot) { if (areFriends(xroot, yroot)) { cout << "-1" << endl; return; } // si x e y son enemigos, entonces // x es amigo de los enemigos de y // y es amigo de los enemigos de x uniteRoot(xroot, eyroot); uniteRoot(yroot, exroot); } int main() { int n, c, x, y; cin >> n; init(n); while (cin >> c >> x >> y) { if (!c && !x && !y) { break; } int xroot = find(x), yroot = find(y); int exroot = find(x + n), eyroot = find(y + n); if (c == 1) { setFriends(xroot, yroot, exroot, eyroot); } else if (c == 2) { setEnemies(xroot, yroot, exroot, eyroot); } else if (c == 3) { cout << (areFriends(xroot, yroot) ? 1 : 0) << endl; } else { cout << (areEnemies(xroot, yroot, exroot, eyroot) ? 1 : 0) << endl; } } return 0; }