ICPC 2022 - Z. Archaeological Recovery
Encode each pyramid configuration as a vector in Z_3^k. Then each lever is also a vector u_j Z_3^k, and pulling a set of levers adds their vectors. The input gives, for each final configuration, how many subsets of th...
Source-first archive entry
This page is built from the copied files in competitive_programming/icpc/2022/Z-archaeological-recovery. Edit
competitive_programming/icpc/2022/Z-archaeological-recovery/solution.tex to update the written solution and
competitive_programming/icpc/2022/Z-archaeological-recovery/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 Z
Archaeological Recovery
Time limit: 1 second
Renowned professor of Egyptology Z Mummer is exploring a newly discovered tomb in Luxor where
she finds a mysterious construction. On a wall, there is a row of k pyramid-shaped stone slabs. Each
stone features three hieroglyphs: an ankh (top), an eye of Horus (bottom left), and an ibis (bottom right).
Next to the wall there are n levers. Cautiously experimenting with these, wary of potential traps, the
professor realizes that each lever rotates some of the pyramids clockwise or counter-clockwise so that
another hieroglyph is pointing upwards on them. Flipping back a lever rotates the same pyramids in the
opposite direction back to their original position. The levers operate completely independently of each
other, so flipping or unflipping a lever has the same effect regardless of what states the other levers are
in. See Figure Z.1 for an illustration.
flip lever 1
flip lever 2 flip lever 2
flip lever 1
Figure Z.1: Illustration of a wall with k = 3 pyramids and n = 2 levers. The first lever
rotates the first pyramid clockwise, the second pyramid counterclockwise, and leaves the
third pyramid unchanged. The second lever rotates all three pyramids clockwise. This
corresponds to Sample Input 1.
Intrigued, Professor Mummer records the individual effect of each lever. Back at her university after
the expedition, she assigns one of her students the laborious task of figuring out all 2n possible pyramid
configurations (some of which may be identical) that can be achieved by flipping some subset of the
levers, so that these can be studied further.
After many nights of careful calculations, the student is finally done and starts gathering his papers. But
then disaster strikes: he accidentally spills some ink and completely destroys the professor’s original
notes, which contained the only record of the individual effects of each lever.
The only chance to escape Professor Mummer’s wrath is to reconstruct the original notes from the list
of possible pyramid configurations. This cannot be done completely unambiguously (for example, there
is no way of distinguishing between different orderings of the same set of levers). But as long as the
levers still result in the calculated list of pyramid configurations, this error is unlikely to be noticed (at
least not until after the student has graduated).
46th ICPC World Championship Problem Z: Archaeological Recovery © ICPC Foundation 21
World Finals | ICPC 2023 Luxor
46th Annual hosted by
ICPC World
Championship AASTMT
Input
The first line of input contains three integers n, k and t, where n (1 ≤ n ≤ 40) and k (1 ≤ k ≤ 5) are
the number of levers and pyramids, respectively, and t (1 ≤ t ≤ 3k ) is the number of different pyramid
configurations. Then follow t lines describing the distinct possible pyramid configurations. Each such
line consists of a string of length k describing the configuration and an integer f (1 ≤ f ≤ 2n ) indicating
the number of different lever settings which result in this configuration. The configuration is described
using the letters ‘A’, ‘E’, and ‘I’. The j th character indicates the hieroglyph facing upwards on the j th
pyramid: ‘A’ for ankh, ‘E’ for eye of Horus, and ‘I’ for ibis.
The t configurations given are pairwise distinct, the sum of f -values over all t lines equals 2n , and the
input is such that at least one list of levers results in the given multiset of configurations.
Output
Output n lines describing a possible set of levers that gives rise to the given multiset of pyramid config-
urations. Each line describes one lever using a string of length k consisting of the symbols ‘+’, ‘-’, or
‘0’. In each such string, the j th symbol describes the action of this lever on the j th pyramid: ‘+’ if the
lever moves the pyramid clockwise, ‘-’ if the lever moves it counterclockwise, and ‘0’ if the lever does
not move this pyramid.
If there is more than one possible solution, any one will be accepted.
Sample Input 1 Sample Output 1
2 3 4 +-0
EEE 1 +++
EIA 1
IAE 1
AAA 1
Sample Input 2 Sample Output 2
3 2 2 -0
IA 4 00
AA 4 00
46th ICPC World Championship Problem Z: Archaeological Recovery © ICPC Foundation 22
Editorial
Rendered from the copied solution.tex file. The original TeX source remains
available below.
Key Observations
For any fixed $x \in \mathbb{Z}_3^k$, taking dot products with $x$ turns the whole problem into a one-dimensional problem over $\mathbb{Z}_3$.
In dimension $1$, the number of zero levers is exactly the exponent of the largest power of $2$ dividing all three residue counts. This lets us recover \[ f(x) = |\{j : u_j \cdot x = 0\}| \] for every $x$.
Averaging $f$ over an orthogonal complement tells us how many levers lie in a given linear subspace. In particular, for each line $\{0,x,-x\}$ we can recover how many levers belong to the pair $\{x,-x\}$.
After that, only the signs are unknown: for every pair $\{x,-x\}$ we know how many copies exist, but not which copies are $x$ and which are $-x$.
Flipping the sign of a subset of levers translates the entire subset-sum multiset by one vector of $\mathbb{Z}_3^k$. So after choosing arbitrary signs, the real answer differs only by a shift of the generated multiset.
Algorithm
Enumerate all $3^k$ vectors of $\mathbb{Z}_3^k$ and precompute:
vector addition modulo $3$,
vector negation,
dot products modulo $3$.
For each $x \in \mathbb{Z}_3^k$, project the observed subset-sum multiset onto the three residues of $s \cdot x$. Let those counts be $c_0,c_1,c_2$. From the one-dimensional fact above, set \[ f(x) = \nu_2(\gcd(c_0,c_1,c_2)), \] where $\nu_2$ is the exponent of the largest power of $2$ dividing its argument.
Recover the multiplicity of the zero vector by averaging $f$ over all $x$: \[ m_{\{0\}} = \frac{1}{2}\left(\frac{3}{3^k}\sum_x f(x) - n\right). \]
For each nonzero line $G = \{0,x,-x\}$, let $H = G^\perp$. Then recover the number of levers in that line by \[ m_G = \frac{1}{2}\left(\frac{3}{|H|}\sum_{y \in H} f(y) - n\right). \] Subtract the zero-vector multiplicity to obtain the number of copies of $\{x,-x\}$.
Choose one canonical representative from each pair $\{x,-x\}$ and create that many copies of it. Together with the zero vectors this gives an arbitrary-sign lever multiset.
Compute the subset-sum multiset of this arbitrary-sign multiset by DP over all $3^k$ states. At the same time, store one witness subset for every reachable sum.
Try every shift vector $v \in \mathbb{Z}_3^k$. If shifting the DP multiset by $v$ matches the target multiset, and $v$ is reachable as a subset sum of the arbitrary-sign levers, flip the signs of exactly the witness subset for $v$ and output the resulting levers. enumerate
Correctness Proof
We prove that the algorithm returns the correct answer.
Lemma 1.
For every $x \in \mathbb{Z}_3^k$, the value $f(x) = |\{j : u_j \cdot x = 0\}|$ is determined by the projected subset-sum counts onto the residues of $s \cdot x$.
Proof.
Fix $x$ and replace every lever vector $u_j$ by the scalar $u_j \cdot x \in \mathbb{Z}_3$. The observed subset sums project to the subset sums of these scalars, so we obtain a one-dimensional instance.
In that one-dimensional instance, every zero lever doubles all three counts, so each zero lever contributes one factor of $2$ to their common gcd. If we remove all zero levers and only keep nonzero scalars, the three counts are never all even; equivalently, the common gcd is odd. Hence the exact number of zero scalars is the largest power of $2$ dividing all three counts, which is precisely $\nu_2(\gcd(c_0,c_1,c_2))$. □
Lemma 2.
For any linear subspace $G \subseteq \mathbb{Z}_3^k$, the values $f(x)$ determine how many levers lie in $G$.
Proof.
Let $H = G^\perp$. Consider \[ \sum_{y \in H} f(y). \] Each lever inside $G$ is orthogonal to every $y \in H$, so it contributes $|H|$ to this sum. A lever outside $G$ is orthogonal to exactly one third of the vectors in $H$, so it contributes $|H|/3$.
If $m_G$ levers lie in $G$, then \[ \sum_{y \in H} f(y) = m_G |H| + (n-m_G)\frac{|H|}{3}. \] Solving for $m_G$ gives \[ m_G = \frac{1}{2}\left(\frac{3}{|H|}\sum_{y \in H} f(y) - n\right), \] which is exactly the formula used by the algorithm. □
Lemma 3.
Once the multiplicity of each pair $\{x,-x\}$ is known, any valid lever multiset differs from an arbitrary sign choice only by flipping the signs of some subset of the chosen levers. Such a sign flip translates the whole subset-sum multiset by a reachable vector.
Proof.
Knowing the multiplicity of each pair $\{x,-x\}$ means that only the sign of each nonzero copy is still undetermined. Therefore any valid solution can be obtained from the arbitrary-sign choice by negating some subset of those copies.
Negating one lever $u$ changes every subset sum by the same translation: the multiset generated by $-u$ is the old multiset shifted by $-u$. Repeating this for several levers shows that negating a subset translates the entire multiset by the sum of the originally chosen vectors in that subset. That translation vector is reachable as a subset sum, and the stored witness subset reconstructs exactly which signs to flip. □
Theorem.
The algorithm outputs a valid multiset of lever vectors.
Proof.
By Lemma 1, the algorithm computes the exact values $f(x)$ for all $x$. By Lemma 2, these values recover the multiplicity of the zero vector and of every pair $\{x,-x\}$, so the only remaining freedom is the choice of signs.
By Lemma 3, every valid sign choice corresponds to a translation of the subset-sum multiset of the arbitrary-sign construction by some reachable vector. The algorithm tests all shifts and, once it finds the matching one, flips exactly the witness subset that realizes that shift. The resulting lever multiset therefore generates exactly the target subset-sum multiset. □
Complexity Analysis
Let $M = 3^k$. Precomputing addition and dot products takes $O(kM^2)$ time and $O(M^2)$ memory. Computing all values $f(x)$ and all line multiplicities takes another $O(M^2)$ time. The subset-sum DP over the reconstructed levers takes $O(nM)$ time and $O(M)$ extra memory. Thus the total running time is $O(kM^2 + nM)$, which is $O(3^{2k} + n3^k)$ up to constant factors, and the memory usage is $O(M^2)$.
Implementation Notes
The states are encoded as base-$3$ integers, but vector addition must still be done coordinate-wise modulo $3$; it is not the same as ordinary integer addition modulo $3^k$.
The DP stores one witness subset for every reachable sum so that the final sign flips can be reconstructed directly.
Code
Exact copied C++ implementation from solution.cpp.
#include <bits/stdc++.h>
using namespace std;
namespace {
using u64 = unsigned long long;
int n, k, t_count;
vector<int> pow3;
vector<vector<int>> coord;
vector<int> neg_vec;
vector<vector<int>> add_vec;
vector<vector<int>> dot_mod;
u64 gcd_u64(u64 a, u64 b) {
while (b != 0) {
u64 t = a % b;
a = b;
b = t;
}
return a;
}
int encode(const vector<int>& values) {
int id = 0;
for (int i = 0; i < k; ++i) {
id += values[i] * pow3[i];
}
return id;
}
int parse_state(const string& s) {
vector<int> values(k);
for (int i = 0; i < k; ++i) {
values[i] = (s[i] == 'A' ? 0 : (s[i] == 'E' ? 1 : 2));
}
return encode(values);
}
string lever_string(int id) {
string s(k, '0');
for (int i = 0; i < k; ++i) {
int value = coord[id][i];
s[i] = (value == 0 ? '0' : (value == 1 ? '+' : '-'));
}
return s;
}
void solve() {
cin >> n >> k >> t_count;
pow3.assign(k + 1, 1);
for (int i = 1; i <= k; ++i) {
pow3[i] = pow3[i - 1] * 3;
}
int states = pow3[k];
coord.assign(states, vector<int>(k, 0));
neg_vec.assign(states, 0);
for (int id = 0; id < states; ++id) {
int x = id;
vector<int> values(k, 0);
for (int i = 0; i < k; ++i) {
values[i] = x % 3;
x /= 3;
}
coord[id] = values;
vector<int> neg_values(k, 0);
for (int i = 0; i < k; ++i) {
neg_values[i] = (3 - values[i]) % 3;
}
neg_vec[id] = encode(neg_values);
}
dot_mod.assign(states, vector<int>(states, 0));
add_vec.assign(states, vector<int>(states, 0));
for (int a = 0; a < states; ++a) {
for (int b = 0; b < states; ++b) {
int value = 0;
int sum_vec = 0;
for (int i = 0; i < k; ++i) {
value += coord[a][i] * coord[b][i];
sum_vec += ((coord[a][i] + coord[b][i]) % 3) * pow3[i];
}
dot_mod[a][b] = value % 3;
add_vec[a][b] = sum_vec;
}
}
vector<u64> target_count(states, 0);
for (int i = 0; i < t_count; ++i) {
string s;
u64 freq;
cin >> s >> freq;
target_count[parse_state(s)] = freq;
}
vector<int> orth_count(states, 0);
for (int x = 0; x < states; ++x) {
u64 cnt[3] = {0, 0, 0};
for (int s = 0; s < states; ++s) {
cnt[dot_mod[s][x]] += target_count[s];
}
u64 g = gcd_u64(cnt[0], gcd_u64(cnt[1], cnt[2]));
orth_count[x] = static_cast<int>(__builtin_ctzll(g));
}
long long sum_all = 0;
for (int x = 0; x < states; ++x) {
sum_all += orth_count[x];
}
int zero_count = static_cast<int>((3LL * sum_all / states - n) / 2);
vector<int> levers;
levers.reserve(n);
for (int i = 0; i < zero_count; ++i) {
levers.push_back(0);
}
for (int x = 1; x < states; ++x) {
if (x > neg_vec[x]) {
continue;
}
long long sum_orth = 0;
for (int y = 0; y < states; ++y) {
if (dot_mod[x][y] == 0) {
sum_orth += orth_count[y];
}
}
int line_count = static_cast<int>((3LL * sum_orth / (states / 3) - n) / 2);
int multiplicity = line_count - zero_count;
for (int i = 0; i < multiplicity; ++i) {
levers.push_back(x);
}
}
vector<u64> dp(states, 0), next_dp(states, 0);
dp[0] = 1;
vector<char> reachable(states, false), next_reachable(states, false);
vector<u64> witness(states, 0), next_witness(states, 0);
reachable[0] = true;
for (int i = 0; i < static_cast<int>(levers.size()); ++i) {
fill(next_dp.begin(), next_dp.end(), 0);
next_reachable = reachable;
next_witness = witness;
int add = levers[i];
for (int s = 0; s < states; ++s) {
next_dp[s] += dp[s];
if (dp[s] != 0) {
int to = add_vec[s][add];
next_dp[to] += dp[s];
}
if (reachable[s]) {
int to = add_vec[s][add];
if (!next_reachable[to]) {
next_reachable[to] = true;
next_witness[to] = witness[s] | (1ULL << i);
}
}
}
dp.swap(next_dp);
reachable.swap(next_reachable);
witness.swap(next_witness);
}
int shift = -1;
for (int v = 0; v < states; ++v) {
if (!reachable[v]) {
continue;
}
bool ok = true;
for (int s = 0; s < states; ++s) {
int shifted = add_vec[s][v];
if (dp[shifted] != target_count[s]) {
ok = false;
break;
}
}
if (ok) {
shift = v;
break;
}
}
vector<int> answer = levers;
if (shift != -1) {
u64 mask = witness[shift];
for (int i = 0; i < static_cast<int>(answer.size()); ++i) {
if ((mask >> i) & 1ULL) {
answer[i] = neg_vec[answer[i]];
}
}
}
for (int id : answer) {
cout << lever_string(id) << '\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\\Z. Archaeological Recovery}
\author{}
\date{}
\begin{document}
\maketitle
\section*{Problem Summary}
Encode each pyramid configuration as a vector in $\mathbb{Z}_3^k$. Then each lever is also a vector
$u_j \in \mathbb{Z}_3^k$, and pulling a set of levers adds their vectors. The input gives, for each final
configuration, how many subsets of the unknown levers produce it. We must reconstruct one valid
multiset of $n$ lever vectors.
\section*{Key Observations}
\begin{itemize}[leftmargin=*]
\item For any fixed $x \in \mathbb{Z}_3^k$, taking dot products with $x$ turns the whole problem
into a one-dimensional problem over $\mathbb{Z}_3$.
\item In dimension $1$, the number of zero levers is exactly the exponent of the largest power of
$2$ dividing all three residue counts. This lets us recover
\[
f(x) = |\{j : u_j \cdot x = 0\}|
\]
for every $x$.
\item Averaging $f$ over an orthogonal complement tells us how many levers lie in a given linear
subspace. In particular, for each line $\{0,x,-x\}$ we can recover how many levers belong to the
pair $\{x,-x\}$.
\item After that, only the signs are unknown: for every pair $\{x,-x\}$ we know how many copies
exist, but not which copies are $x$ and which are $-x$.
\item Flipping the sign of a subset of levers translates the entire subset-sum multiset by one vector
of $\mathbb{Z}_3^k$. So after choosing arbitrary signs, the real answer differs only by a shift of
the generated multiset.
\end{itemize}
\section*{Algorithm}
\begin{enumerate}[leftmargin=*]
\item Enumerate all $3^k$ vectors of $\mathbb{Z}_3^k$ and precompute:
\begin{itemize}[leftmargin=*]
\item vector addition modulo $3$,
\item vector negation,
\item dot products modulo $3$.
\end{itemize}
\item For each $x \in \mathbb{Z}_3^k$, project the observed subset-sum multiset onto the three
residues of $s \cdot x$. Let those counts be $c_0,c_1,c_2$. From the one-dimensional fact above,
set
\[
f(x) = \nu_2(\gcd(c_0,c_1,c_2)),
\]
where $\nu_2$ is the exponent of the largest power of $2$ dividing its argument.
\item Recover the multiplicity of the zero vector by averaging $f$ over all $x$:
\[
m_{\{0\}} =
\frac{1}{2}\left(\frac{3}{3^k}\sum_x f(x) - n\right).
\]
\item For each nonzero line $G = \{0,x,-x\}$, let $H = G^\perp$. Then recover the number of
levers in that line by
\[
m_G =
\frac{1}{2}\left(\frac{3}{|H|}\sum_{y \in H} f(y) - n\right).
\]
Subtract the zero-vector multiplicity to obtain the number of copies of $\{x,-x\}$.
\item Choose one canonical representative from each pair $\{x,-x\}$ and create that many copies
of it. Together with the zero vectors this gives an arbitrary-sign lever multiset.
\item Compute the subset-sum multiset of this arbitrary-sign multiset by DP over all $3^k$ states.
At the same time, store one witness subset for every reachable sum.
\item Try every shift vector $v \in \mathbb{Z}_3^k$. If shifting the DP multiset by $v$ matches the
target multiset, and $v$ is reachable as a subset sum of the arbitrary-sign levers, flip the signs of
exactly the witness subset for $v$ and output the resulting levers.
\end{enumerate}
\section*{Correctness Proof}
We prove that the algorithm returns the correct answer.
\paragraph{Lemma 1.}
For every $x \in \mathbb{Z}_3^k$, the value $f(x) = |\{j : u_j \cdot x = 0\}|$ is determined by the
projected subset-sum counts onto the residues of $s \cdot x$.
\paragraph{Proof.}
Fix $x$ and replace every lever vector $u_j$ by the scalar $u_j \cdot x \in \mathbb{Z}_3$. The observed
subset sums project to the subset sums of these scalars, so we obtain a one-dimensional instance.
In that one-dimensional instance, every zero lever doubles all three counts, so each zero lever
contributes one factor of $2$ to their common gcd. If we remove all zero levers and only keep
nonzero scalars, the three counts are never all even; equivalently, the common gcd is odd. Hence
the exact number of zero scalars is the largest power of $2$ dividing all three counts, which is
precisely $\nu_2(\gcd(c_0,c_1,c_2))$. \qed
\paragraph{Lemma 2.}
For any linear subspace $G \subseteq \mathbb{Z}_3^k$, the values $f(x)$ determine how many levers
lie in $G$.
\paragraph{Proof.}
Let $H = G^\perp$. Consider
\[
\sum_{y \in H} f(y).
\]
Each lever inside $G$ is orthogonal to every $y \in H$, so it contributes $|H|$ to this sum. A lever
outside $G$ is orthogonal to exactly one third of the vectors in $H$, so it contributes $|H|/3$.
If $m_G$ levers lie in $G$, then
\[
\sum_{y \in H} f(y) = m_G |H| + (n-m_G)\frac{|H|}{3}.
\]
Solving for $m_G$ gives
\[
m_G =
\frac{1}{2}\left(\frac{3}{|H|}\sum_{y \in H} f(y) - n\right),
\]
which is exactly the formula used by the algorithm. \qed
\paragraph{Lemma 3.}
Once the multiplicity of each pair $\{x,-x\}$ is known, any valid lever multiset differs from an
arbitrary sign choice only by flipping the signs of some subset of the chosen levers. Such a sign flip
translates the whole subset-sum multiset by a reachable vector.
\paragraph{Proof.}
Knowing the multiplicity of each pair $\{x,-x\}$ means that only the sign of each nonzero copy is
still undetermined. Therefore any valid solution can be obtained from the arbitrary-sign choice by
negating some subset of those copies.
Negating one lever $u$ changes every subset sum by the same translation: the multiset generated by
$-u$ is the old multiset shifted by $-u$. Repeating this for several levers shows that negating a
subset translates the entire multiset by the sum of the originally chosen vectors in that subset.
That translation vector is reachable as a subset sum, and the stored witness subset reconstructs
exactly which signs to flip. \qed
\paragraph{Theorem.}
The algorithm outputs a valid multiset of lever vectors.
\paragraph{Proof.}
By Lemma 1, the algorithm computes the exact values $f(x)$ for all $x$. By Lemma 2, these values
recover the multiplicity of the zero vector and of every pair $\{x,-x\}$, so the only remaining
freedom is the choice of signs.
By Lemma 3, every valid sign choice corresponds to a translation of the subset-sum multiset of the
arbitrary-sign construction by some reachable vector. The algorithm tests all shifts and, once it finds
the matching one, flips exactly the witness subset that realizes that shift. The resulting lever multiset
therefore generates exactly the target subset-sum multiset. \qed
\section*{Complexity Analysis}
Let $M = 3^k$. Precomputing addition and dot products takes $O(kM^2)$ time and $O(M^2)$ memory.
Computing all values $f(x)$ and all line multiplicities takes another $O(M^2)$ time. The subset-sum
DP over the reconstructed levers takes $O(nM)$ time and $O(M)$ extra memory. Thus the total
running time is $O(kM^2 + nM)$, which is $O(3^{2k} + n3^k)$ up to constant factors, and the memory
usage is $O(M^2)$.
\section*{Implementation Notes}
\begin{itemize}[leftmargin=*]
\item The states are encoded as base-$3$ integers, but vector addition must still be done
coordinate-wise modulo $3$; it is not the same as ordinary integer addition modulo $3^k$.
\item The DP stores one witness subset for every reachable sum so that the final sign flips can be
reconstructed directly.
\end{itemize}
\end{document}
#include <bits/stdc++.h>
using namespace std;
namespace {
using u64 = unsigned long long;
int n, k, t_count;
vector<int> pow3;
vector<vector<int>> coord;
vector<int> neg_vec;
vector<vector<int>> add_vec;
vector<vector<int>> dot_mod;
u64 gcd_u64(u64 a, u64 b) {
while (b != 0) {
u64 t = a % b;
a = b;
b = t;
}
return a;
}
int encode(const vector<int>& values) {
int id = 0;
for (int i = 0; i < k; ++i) {
id += values[i] * pow3[i];
}
return id;
}
int parse_state(const string& s) {
vector<int> values(k);
for (int i = 0; i < k; ++i) {
values[i] = (s[i] == 'A' ? 0 : (s[i] == 'E' ? 1 : 2));
}
return encode(values);
}
string lever_string(int id) {
string s(k, '0');
for (int i = 0; i < k; ++i) {
int value = coord[id][i];
s[i] = (value == 0 ? '0' : (value == 1 ? '+' : '-'));
}
return s;
}
void solve() {
cin >> n >> k >> t_count;
pow3.assign(k + 1, 1);
for (int i = 1; i <= k; ++i) {
pow3[i] = pow3[i - 1] * 3;
}
int states = pow3[k];
coord.assign(states, vector<int>(k, 0));
neg_vec.assign(states, 0);
for (int id = 0; id < states; ++id) {
int x = id;
vector<int> values(k, 0);
for (int i = 0; i < k; ++i) {
values[i] = x % 3;
x /= 3;
}
coord[id] = values;
vector<int> neg_values(k, 0);
for (int i = 0; i < k; ++i) {
neg_values[i] = (3 - values[i]) % 3;
}
neg_vec[id] = encode(neg_values);
}
dot_mod.assign(states, vector<int>(states, 0));
add_vec.assign(states, vector<int>(states, 0));
for (int a = 0; a < states; ++a) {
for (int b = 0; b < states; ++b) {
int value = 0;
int sum_vec = 0;
for (int i = 0; i < k; ++i) {
value += coord[a][i] * coord[b][i];
sum_vec += ((coord[a][i] + coord[b][i]) % 3) * pow3[i];
}
dot_mod[a][b] = value % 3;
add_vec[a][b] = sum_vec;
}
}
vector<u64> target_count(states, 0);
for (int i = 0; i < t_count; ++i) {
string s;
u64 freq;
cin >> s >> freq;
target_count[parse_state(s)] = freq;
}
vector<int> orth_count(states, 0);
for (int x = 0; x < states; ++x) {
u64 cnt[3] = {0, 0, 0};
for (int s = 0; s < states; ++s) {
cnt[dot_mod[s][x]] += target_count[s];
}
u64 g = gcd_u64(cnt[0], gcd_u64(cnt[1], cnt[2]));
orth_count[x] = static_cast<int>(__builtin_ctzll(g));
}
long long sum_all = 0;
for (int x = 0; x < states; ++x) {
sum_all += orth_count[x];
}
int zero_count = static_cast<int>((3LL * sum_all / states - n) / 2);
vector<int> levers;
levers.reserve(n);
for (int i = 0; i < zero_count; ++i) {
levers.push_back(0);
}
for (int x = 1; x < states; ++x) {
if (x > neg_vec[x]) {
continue;
}
long long sum_orth = 0;
for (int y = 0; y < states; ++y) {
if (dot_mod[x][y] == 0) {
sum_orth += orth_count[y];
}
}
int line_count = static_cast<int>((3LL * sum_orth / (states / 3) - n) / 2);
int multiplicity = line_count - zero_count;
for (int i = 0; i < multiplicity; ++i) {
levers.push_back(x);
}
}
vector<u64> dp(states, 0), next_dp(states, 0);
dp[0] = 1;
vector<char> reachable(states, false), next_reachable(states, false);
vector<u64> witness(states, 0), next_witness(states, 0);
reachable[0] = true;
for (int i = 0; i < static_cast<int>(levers.size()); ++i) {
fill(next_dp.begin(), next_dp.end(), 0);
next_reachable = reachable;
next_witness = witness;
int add = levers[i];
for (int s = 0; s < states; ++s) {
next_dp[s] += dp[s];
if (dp[s] != 0) {
int to = add_vec[s][add];
next_dp[to] += dp[s];
}
if (reachable[s]) {
int to = add_vec[s][add];
if (!next_reachable[to]) {
next_reachable[to] = true;
next_witness[to] = witness[s] | (1ULL << i);
}
}
}
dp.swap(next_dp);
reachable.swap(next_reachable);
witness.swap(next_witness);
}
int shift = -1;
for (int v = 0; v < states; ++v) {
if (!reachable[v]) {
continue;
}
bool ok = true;
for (int s = 0; s < states; ++s) {
int shifted = add_vec[s][v];
if (dp[shifted] != target_count[s]) {
ok = false;
break;
}
}
if (ok) {
shift = v;
break;
}
}
vector<int> answer = levers;
if (shift != -1) {
u64 mask = witness[shift];
for (int i = 0; i < static_cast<int>(answer.size()); ++i) {
if ((mask >> i) & 1ULL) {
answer[i] = neg_vec[answer[i]];
}
}
}
for (int id : answer) {
cout << lever_string(id) << '\n';
}
}
} // namespace
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
return 0;
}