ICPC 2022 - P. Turning Red
Each button press increases the color of every controlled light by 1 modulo 3, using the encoding red =0, green =1, blue =2. Each light is controlled by at most two buttons. We must turn every light red while minimizi...
Source-first archive entry
This page is built from the copied files in competitive_programming/icpc/2022/P-turning-red. Edit
competitive_programming/icpc/2022/P-turning-red/solution.tex to update the written solution and
competitive_programming/icpc/2022/P-turning-red/solution.cpp to update the implementation.
The website does not replace those files with hand-maintained HTML. It reads the copied source tree during the build and exposes the exact files below.
Problem Statement
Copied statement text kept beside the solution archive for direct reference.
World Finals | ICPC 2023 Luxor
46th Annual hosted by
ICPC World
Championship AASTMT
Problem P
Turning Red
Time limit: 3 seconds
Mei’s parents have spent the last year remodeling their house, but their lighting system is quite complex!
Each room in the house has an LED light, which can be set to red, green, or blue, as seen in Figure P.1.
G B R B R R R G
Figure P.1: The initial state of the lights in Sample Input 1. Buttons and wires not shown.
Throughout the house are various buttons which are each connected to one or more lights. When a
button is pressed, any red lights connected to that button become green, any green lights connected to
that button become blue, and any blue lights connected to that button become red. Each button can be
pressed multiple times. Because the house was built prior to the invention of crossbar wiring, each light
is controlled by at most two buttons.
Mei’s favorite color is red, so she wants to turn all of the lights red. Her parents, fearing the buttons will
wear out, have asked her to minimize the total number of button presses.
Input
The first line of input contains two positive integers l and b, where l (1 ≤ l ≤ 2 · 105 ) is the number of
lights and b (0 ≤ b ≤ 2 · l) is the number of buttons. The second line of input is a string of l characters,
all either R, G, or B, where the ith character is the initial color of the ith light. The next b lines describe
the buttons. Each of these lines begins with an integer k (1 ≤ k ≤ l), the number of lights controlled by
this button. Then k distinct integers follow, the lights controlled by this button. The lights are indexed
from 1 to l, inclusive. Each light appears at most twice across all buttons.
Output
Output the minimum number of button presses Mei needs to turn all the lights red. If it is impossible for
Mei to turn all of the lights red, output impossible.
Sample Input 1 Sample Output 1
8 6 8
GBRBRRRG
2 1 4
1 2
4 4 5 6 7
3 5 6 7
1 8
1 8
46th ICPC World Championship Problem P: Turning Red © ICPC Foundation 1
World Finals | ICPC 2023 Luxor
46th Annual hosted by
ICPC World
Championship AASTMT
Sample Input 2 Sample Output 2
4 3 impossible
RGBR
2 1 2
2 2 3
2 3 4
Sample Input 3 Sample Output 3
4 4 6
GBRG
2 1 2
2 2 3
2 3 4
1 4
Sample Input 4 Sample Output 4
3 3 3
RGB
1 1
1 2
1 3
46th ICPC World Championship Problem P: Turning Red © ICPC Foundation 2
Editorial
Rendered from the copied solution.tex file. The original TeX source remains
available below.
Key Observations
Let $x_i \in \{0,1,2\}$ be the number of times button $i$ is pressed, modulo $3$.
For a light controlled by buttons $u$ and $v$, the requirement that it becomes red is one linear equation \[ x_u+x_v \equiv \text{need} \pmod 3. \] If a light is controlled by only one button $u$, we simply get $x_u \equiv \text{need} \pmod 3$.
Therefore each connected component of the graph on buttons, where two buttons are adjacent if they control the same 2-button light, can be solved independently.
Inside one component, once the value of one button is fixed, every other button value is forced by BFS propagation through the equations.
There are only three possible values for the root button, so we can try all three and keep the valid one with minimum total presses.
Algorithm
Convert every initial light color into the residue
needrequired to reach red modulo $3$.Build the incidence structure between buttons and lights.
Traverse the connected components of the button graph.
For each component, try root values $0,1,2$:
propagate all forced values with BFS;
reject the choice if some equation becomes inconsistent;
otherwise compute the sum of button values in that component.
If no root value works for some component, output
impossible. Otherwise sum the best component costs. enumerateCorrectness Proof
We prove that the algorithm returns the correct answer.
Lemma 1.
Within one connected component of the button graph, fixing the value of one button determines every other button value uniquely.
Proof.
Whenever a light is controlled by buttons $u$ and $v$, the equation \[ x_u+x_v \equiv \text{need} \pmod 3 \] implies \[ x_v \equiv \text{need}-x_u \pmod 3. \] So knowing one endpoint forces the other. Repeating this along paths determines every button in the component uniquely. □
Lemma 2.
For a fixed root value in one component, the BFS propagation finds a valid assignment if and only if such an assignment exists with that root value.
Proof.
By Lemma 1, every button value is uniquely forced once the root value is fixed. The BFS computes exactly those forced values. If a contradiction appears, then some light imposes two different values on the same button, so no valid assignment with this root value exists. If no contradiction appears, all encountered equations are satisfied, so the computed assignment is valid. □
Theorem.
The algorithm outputs the minimum total number of button presses, or
impossibleif no solution exists.Proof.
Different connected components share no variables, so the total cost is the sum of independent component costs. For one component, every feasible solution corresponds to exactly one of the three root values modulo $3$. By Lemma 2, for each root value the algorithm either proves infeasibility or constructs the unique valid assignment. Taking the minimum valid cost in each component is therefore optimal, and if some component has no valid root value then no global solution exists. □
Complexity Analysis
Each light-button incidence is processed only a constant number of times across all traversals. Therefore the running time is linear in the input size, namely $O(l+b+\text{total incidences})$, and the memory usage is the same order.
Implementation Notes
A light controlled by zero buttons is immediately fatal unless it is already red.
The optimal number of presses for one button is always one of $0,1,2$, because three extra presses have no effect.
Code
Exact copied C++ implementation from solution.cpp.
#include <bits/stdc++.h>
using namespace std;
namespace {
int color_value(char c) {
if (c == 'R') {
return 0;
}
if (c == 'G') {
return 1;
}
return 2;
}
int mod3(int x) {
x %= 3;
if (x < 0) {
x += 3;
}
return x;
}
void solve() {
int l, b;
cin >> l >> b;
string colors;
cin >> colors;
vector<vector<int>> lights_of_button(b);
vector<vector<int>> buttons_of_light(l);
for (int i = 0; i < b; ++i) {
int k;
cin >> k;
lights_of_button[i].resize(k);
for (int j = 0; j < k; ++j) {
int light;
cin >> light;
--light;
lights_of_button[i][j] = light;
buttons_of_light[light].push_back(i);
}
}
vector<int> need(l);
for (int i = 0; i < l; ++i) {
need[i] = mod3(-color_value(colors[i]));
if (buttons_of_light[i].empty() && need[i] != 0) {
cout << "impossible\n";
return;
}
}
vector<char> seen(b, false);
long long answer = 0;
vector<int> value(b, -1);
vector<int> component;
queue<int> q;
for (int start = 0; start < b; ++start) {
if (seen[start]) {
continue;
}
component.clear();
seen[start] = true;
q.push(start);
while (!q.empty()) {
int u = q.front();
q.pop();
component.push_back(u);
for (int light : lights_of_button[u]) {
if (buttons_of_light[light].size() != 2) {
continue;
}
int v = buttons_of_light[light][0] ^ buttons_of_light[light][1] ^ u;
if (!seen[v]) {
seen[v] = true;
q.push(v);
}
}
}
long long best_cost = -1;
vector<int> best_assignment;
for (int root_value = 0; root_value < 3; ++root_value) {
for (int node : component) {
value[node] = -1;
}
queue<int> bfs;
value[start] = root_value;
bfs.push(start);
bool ok = true;
while (!bfs.empty() && ok) {
int u = bfs.front();
bfs.pop();
for (int light : lights_of_button[u]) {
int target = need[light];
const auto& owners = buttons_of_light[light];
if (owners.size() == 1) {
if (value[u] != target) {
ok = false;
break;
}
continue;
}
int v = owners[0] ^ owners[1] ^ u;
int required = mod3(target - value[u]);
if (value[v] == -1) {
value[v] = required;
bfs.push(v);
} else if (value[v] != required) {
ok = false;
break;
}
}
}
if (!ok) {
continue;
}
long long cost = 0;
for (int node : component) {
cost += value[node];
}
if (best_cost == -1 || cost < best_cost) {
best_cost = cost;
best_assignment.clear();
best_assignment.reserve(component.size());
for (int node : component) {
best_assignment.push_back(value[node]);
}
}
}
if (best_cost == -1) {
cout << "impossible\n";
return;
}
answer += best_cost;
for (int i = 0; i < static_cast<int>(component.size()); ++i) {
value[component[i]] = best_assignment[i];
}
}
cout << answer << '\n';
}
} // namespace
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
return 0;
}
Source Files
Exact copied source-of-truth files. Edit solution.tex for the write-up and solution.cpp for the implementation.
\documentclass[11pt]{article}
\usepackage[margin=1in]{geometry}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{amsmath,amssymb,amsthm}
\usepackage{enumitem}
\title{ICPC World Finals 2022\\P. Turning Red}
\author{}
\date{}
\begin{document}
\maketitle
\section*{Problem Summary}
Each button press increases the color of every controlled light by $1$ modulo $3$, using the encoding
red $=0$, green $=1$, blue $=2$. Each light is controlled by at most two buttons. We must turn every
light red while minimizing the total number of button presses.
\section*{Key Observations}
\begin{itemize}[leftmargin=*]
\item Let $x_i \in \{0,1,2\}$ be the number of times button $i$ is pressed, modulo $3$.
\item For a light controlled by buttons $u$ and $v$, the requirement that it becomes red is one linear
equation
\[
x_u+x_v \equiv \text{need} \pmod 3.
\]
If a light is controlled by only one button $u$, we simply get $x_u \equiv \text{need} \pmod 3$.
\item Therefore each connected component of the graph on buttons, where two buttons are adjacent if
they control the same 2-button light, can be solved independently.
\item Inside one component, once the value of one button is fixed, every other button value is forced by
BFS propagation through the equations.
\item There are only three possible values for the root button, so we can try all three and keep the valid
one with minimum total presses.
\end{itemize}
\section*{Algorithm}
\begin{enumerate}[leftmargin=*]
\item Convert every initial light color into the residue \texttt{need} required to reach red modulo $3$.
\item Build the incidence structure between buttons and lights.
\item Traverse the connected components of the button graph.
\item For each component, try root values $0,1,2$:
\begin{itemize}[leftmargin=*]
\item propagate all forced values with BFS;
\item reject the choice if some equation becomes inconsistent;
\item otherwise compute the sum of button values in that component.
\end{itemize}
\item If no root value works for some component, output \texttt{impossible}. Otherwise sum the best
component costs.
\end{enumerate}
\section*{Correctness Proof}
We prove that the algorithm returns the correct answer.
\paragraph{Lemma 1.}
Within one connected component of the button graph, fixing the value of one button determines every
other button value uniquely.
\paragraph{Proof.}
Whenever a light is controlled by buttons $u$ and $v$, the equation
\[
x_u+x_v \equiv \text{need} \pmod 3
\]
implies
\[
x_v \equiv \text{need}-x_u \pmod 3.
\]
So knowing one endpoint forces the other. Repeating this along paths determines every button in the
component uniquely. \qed
\paragraph{Lemma 2.}
For a fixed root value in one component, the BFS propagation finds a valid assignment if and only if such
an assignment exists with that root value.
\paragraph{Proof.}
By Lemma 1, every button value is uniquely forced once the root value is fixed. The BFS computes exactly
those forced values. If a contradiction appears, then some light imposes two different values on the same
button, so no valid assignment with this root value exists. If no contradiction appears, all encountered
equations are satisfied, so the computed assignment is valid. \qed
\paragraph{Theorem.}
The algorithm outputs the minimum total number of button presses, or \texttt{impossible} if no solution
exists.
\paragraph{Proof.}
Different connected components share no variables, so the total cost is the sum of independent component
costs. For one component, every feasible solution corresponds to exactly one of the three root values
modulo $3$. By Lemma 2, for each root value the algorithm either proves infeasibility or constructs the
unique valid assignment. Taking the minimum valid cost in each component is therefore optimal, and if
some component has no valid root value then no global solution exists. \qed
\section*{Complexity Analysis}
Each light-button incidence is processed only a constant number of times across all traversals. Therefore
the running time is linear in the input size, namely $O(l+b+\text{total incidences})$, and the memory
usage is the same order.
\section*{Implementation Notes}
\begin{itemize}[leftmargin=*]
\item A light controlled by zero buttons is immediately fatal unless it is already red.
\item The optimal number of presses for one button is always one of $0,1,2$, because three extra
presses have no effect.
\end{itemize}
\end{document}
#include <bits/stdc++.h>
using namespace std;
namespace {
int color_value(char c) {
if (c == 'R') {
return 0;
}
if (c == 'G') {
return 1;
}
return 2;
}
int mod3(int x) {
x %= 3;
if (x < 0) {
x += 3;
}
return x;
}
void solve() {
int l, b;
cin >> l >> b;
string colors;
cin >> colors;
vector<vector<int>> lights_of_button(b);
vector<vector<int>> buttons_of_light(l);
for (int i = 0; i < b; ++i) {
int k;
cin >> k;
lights_of_button[i].resize(k);
for (int j = 0; j < k; ++j) {
int light;
cin >> light;
--light;
lights_of_button[i][j] = light;
buttons_of_light[light].push_back(i);
}
}
vector<int> need(l);
for (int i = 0; i < l; ++i) {
need[i] = mod3(-color_value(colors[i]));
if (buttons_of_light[i].empty() && need[i] != 0) {
cout << "impossible\n";
return;
}
}
vector<char> seen(b, false);
long long answer = 0;
vector<int> value(b, -1);
vector<int> component;
queue<int> q;
for (int start = 0; start < b; ++start) {
if (seen[start]) {
continue;
}
component.clear();
seen[start] = true;
q.push(start);
while (!q.empty()) {
int u = q.front();
q.pop();
component.push_back(u);
for (int light : lights_of_button[u]) {
if (buttons_of_light[light].size() != 2) {
continue;
}
int v = buttons_of_light[light][0] ^ buttons_of_light[light][1] ^ u;
if (!seen[v]) {
seen[v] = true;
q.push(v);
}
}
}
long long best_cost = -1;
vector<int> best_assignment;
for (int root_value = 0; root_value < 3; ++root_value) {
for (int node : component) {
value[node] = -1;
}
queue<int> bfs;
value[start] = root_value;
bfs.push(start);
bool ok = true;
while (!bfs.empty() && ok) {
int u = bfs.front();
bfs.pop();
for (int light : lights_of_button[u]) {
int target = need[light];
const auto& owners = buttons_of_light[light];
if (owners.size() == 1) {
if (value[u] != target) {
ok = false;
break;
}
continue;
}
int v = owners[0] ^ owners[1] ^ u;
int required = mod3(target - value[u]);
if (value[v] == -1) {
value[v] = required;
bfs.push(v);
} else if (value[v] != required) {
ok = false;
break;
}
}
}
if (!ok) {
continue;
}
long long cost = 0;
for (int node : component) {
cost += value[node];
}
if (best_cost == -1 || cost < best_cost) {
best_cost = cost;
best_assignment.clear();
best_assignment.reserve(component.size());
for (int node : component) {
best_assignment.push_back(value[node]);
}
}
}
if (best_cost == -1) {
cout << "impossible\n";
return;
}
answer += best_cost;
for (int i = 0; i < static_cast<int>(component.size()); ++i) {
value[component[i]] = best_assignment[i];
}
}
cout << answer << '\n';
}
} // namespace
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
return 0;
}