Editorial for IOI '14 P3 - Game
Submitting an official solution before solving the problem yourself is a bannable offence.
Let be the set of edges about which the contestant has answered "yes" (connected),
the set of edges about which contestant has answered "no", and
the rest of the edges, whose statuses are not yet determined. Also, let
and
.
is the graph you get by assuming that all edges in
are not connected, while
is the graph you get by assuming that all edges in
are connected.
Initially, is empty and thus not connected, while
is connected. In order not to reveal any clue to the judge, the contestant should maintain the invariant:
should always be disconnected, while
should always be connected.
There are several possible ways to maintain the invariant.
An
solution
When asked by the judge whether an edge is connected, answer "no" if and only if
is part of a cycle in
. One can see that this does not change the connectivity of
and
.
To decide whether forms a circle, one can perform a depth-first search to find out whether there is a path from
to
in
. This is an
operation. As there are
edges, the total running time is
.
In other words, we answer "yes" if and only if is a bridge in
.
An
solution
Given a vertex , let
be the connected component
belongs to in
. We maintain two data structures:
is a table mapping each
to a representative of
.
is a symmetric matrix indexed by
. For
and
in
, if
,
is the number of edges, in
, that connects
and
.
The contestant answers "yes" to query if and only if
.
can be implemented as a disjoint-set linked list. Each disjoint set is represented by a linked list of its elements, and the representative is the one at the head. Each element has a pointer to its representative. To unite two sets, we connect the lists and update the pointers. A union takes
time and a find takes
time.
As for , initially
unless
. Whenever the judge asks about
,
is updated as follows.
- If the contestant answers "no", we decrement
by
.
- If the contestant answers "yes", let
be the representative after uniting
and
. For each
that is a representative of some connected component, both
and
are updated to
.
There can be at most unions, thus the total time spent on union is
. An update of
requires
time for a "no" response, and
time for a "yes" response. Since the graph
is a tree, we respond "yes" exactly
times. Thus the time spent on updating
is also
. We thus have an
algorithm.
A One-Liner
Algorithm
There is a surprising one-line algorithm:
#include "game.h"
void initialize(int n) {
// DO NOTHING!
}
int c[1500];
int hasEdge(int u, int v) {
return ++c[u > v ? u : v] == (u > v ? u : v);
}
To understand the algorithm, imagine that we partition the set of all the possible edges into , with
. Each
has exactly
possible edges. The algorithm above answers "yes" to
(where
) if it is the last edge in
that is queried.
To see how it works, consider the last query. Denote the queried edge by , and the graph
. The contestant wins if
is disconnected, while
is connected.
is disconnected, since it contains only
edges.
is connected, since it contains
edges, and there is no cycle in
. One can see that there is no cycle since, in each
, we answer yes to only one edge. Formally, if there is a cycle
in
, considering the node
in
with largest id,
must have exactly one edge in
. But
has two neighbors in
with smaller ids, a contradiction.
Comments