Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Think in terms of hidden cell bits , with the outside fixed to . Put a knitted edge exactly when the two regions on its sides have different hidden bits.
For a cell, the four copies of its own hidden bit cancel. So its color parity is
where missing neighbors are .
At an interior grid point surrounded by
the degree is bad exactly when and . So the real condition is
Define diagonal XORs on grid points:
The input gives chains:
Every anti-diagonal of and every main diagonal of has one Boolean variable. Inner grid points give clauses . Boundary diagonal edges touching the same boundary cell must agree. That is all 2-SAT.
Hidden cell bits
A set of disjoint cycles is an Eulerian subgraph of the grid. For a planar grid, represent it as a boundary between hidden face bits.
Put a bit on every cell. The outside of the rectangle has bit . A grid edge is knitted iff the two regions touching it have different bits.
For cell , the frog is toggled by the four side edges. The parity is
The four terms cancel, so
with missing cells equal to . Brown means , green means .
The vertex condition
Around an interior grid point, write the four cell bits as
The selected degree is the number of transitions around . It can only be .
Degree means all four bits are equal. Degree means a checkerboard. Both bad cases are exactly
So the point is valid iff
That tiny OR is the 2-SAT hiding under the knitting. Classic 3500 behavior: cute story, knife behind the back.
Diagonal XORs
Define values on grid points:
again treating missing cells as outside bit .
At an interior grid point, and are exactly the two diagonal XORs, so the coverage condition is simply
For each input cell ,
and also
So every anti-diagonal of is determined by one free bit, and every main diagonal of is determined by one free bit.
Boundary compatibility
Here is the easy-to-miss part. Not every arbitrary choice of these diagonal variables comes from real cell bits .
If a diagonal XOR connects a boundary cell to the outside, its value must equal that boundary cell's bit. Therefore all such diagonal XORs touching the same boundary cell must be equal. If a diagonal XOR connects outside to outside, it must be .
These are just unit clauses and equality constraints between affine literals, so still 2-SAT.
The 2-SAT instance
Create one variable for each anti-diagonal , and one for each main diagonal .
After propagating the known XOR differences along each diagonal, every value looks like
Add:
If 2-SAT fails, answer NO.
Recovering the cells
After solving, compute actual and values.
Recover using diagonals:
The boundary compatibility plus the input recurrences force the recovered cells to satisfy all values too. In the code, we still verify this because bugs deserve zero trust.
Output
A horizontal segment is selected iff the cell above and below differ. A vertical segment is selected iff the cell left and right differ. Outside is .
The inner clauses guarantee every inner grid point has degree exactly . Since the edge set is a boundary of cell bits, every vertex has even degree, so the selected edges decompose into disjoint cycles. Done.
Complexity is per test case.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct TwoSAT {
int n;
vector<vector<int>> g, rg;
TwoSAT(int n = 0) : n(n), g(2 * n), rg(2 * n) {}
int id(int v, int val) const { return 2 * v + val; }
void imply(int a, int b) {
g[a].push_back(b);
rg[b].push_back(a);
}
void add_or(int x, int xv, int y, int yv) {
imply(id(x, xv ^ 1), id(y, yv));
imply(id(y, yv ^ 1), id(x, xv));
}
void add_unit(int x, int xv) {
imply(id(x, xv ^ 1), id(x, xv));
}
void add_equal_expr(int x, int xc, int y, int yc) {
// (value[x] ^ xc) == (value[y] ^ yc)
add_or(x, xc, y, yc ^ 1);
add_or(y, yc, x, xc ^ 1);
}
bool solve(vector<int>& ans) {
int m = 2 * n;
vector<int> order, comp(m, -1), vis(m, 0);
order.reserve(m);
for (int s = 0; s < m; s++) if (!vis[s]) {
vector<pair<int, int>> st = {{s, 0}};
vis[s] = 1;
while (!st.empty()) {
int v = st.back().first;
int &it = st.back().second;
if (it < (int)g[v].size()) {
int to = g[v][it++];
if (!vis[to]) {
vis[to] = 1;
st.push_back({to, 0});
}
} else {
order.push_back(v);
st.pop_back();
}
}
}
int cc = 0;
for (int z = m - 1; z >= 0; z--) {
int s = order[z];
if (comp[s] != -1) continue;
vector<int> st = {s};
comp[s] = cc;
while (!st.empty()) {
int v = st.back();
st.pop_back();
for (int to : rg[v]) if (comp[to] == -1) {
comp[to] = cc;
st.push_back(to);
}
}
cc++;
}
ans.assign(n, 0);
for (int i = 0; i < n; i++) {
if (comp[2 * i] == comp[2 * i + 1]) return false;
ans[i] = comp[2 * i] < comp[2 * i + 1];
}
return true;
}
};
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int r, c;
cin >> r >> c;
vector<string> a(r);
for (string &s : a) cin >> s;
int diagCnt = r + c + 1;
int offQ = diagCnt;
int vars = 2 * diagCnt;
vector<vector<int>> pc(r + 1, vector<int>(c + 1, 0));
vector<vector<int>> qc(r + 1, vector<int>(c + 1, 0));
for (int s = 0; s <= r + c; s++) {
vector<pair<int, int>> pts;
for (int i = max(0, s - c); i <= min(r, s); i++) {
pts.push_back({i, s - i});
}
for (int t = 0; t + 1 < (int)pts.size(); t++) {
auto [i, j] = pts[t];
auto [ni, nj] = pts[t + 1];
pc[ni][nj] = pc[i][j] ^ (a[i][j - 1] == 'B');
}
}
for (int d = -r; d <= c; d++) {
vector<pair<int, int>> pts;
for (int i = max(0, -d); i <= min(r, c - d); i++) {
pts.push_back({i, i + d});
}
for (int t = 0; t + 1 < (int)pts.size(); t++) {
auto [i, j] = pts[t];
auto [ni, nj] = pts[t + 1];
qc[ni][nj] = qc[i][j] ^ (a[i][j] == 'B');
}
}
TwoSAT sat(vars);
auto exprP = [&](int i, int j) -> pair<int, int> {
return {i + j, pc[i][j]};
};
auto exprQ = [&](int i, int j) -> pair<int, int> {
return {offQ + (j - i + r), qc[i][j]};
};
auto cell = [&](int i, int j) -> int {
if (i < 0 || i >= r || j < 0 || j >= c) return -1;
return i * c + j;
};
vector<int> firstVar(r * c, -1), firstConst(r * c, 0);
auto add_boundary_expr = [&](pair<int, int> e, int u, int v) {
if (u == -1 && v == -1) {
sat.add_unit(e.first, e.second);
} else if ((u == -1) != (v == -1)) {
int id = (u == -1 ? v : u);
if (firstVar[id] == -1) {
firstVar[id] = e.first;
firstConst[id] = e.second;
} else {
sat.add_equal_expr(firstVar[id], firstConst[id], e.first, e.second);
}
}
};
for (int i = 0; i <= r; i++) {
for (int j = 0; j <= c; j++) {
add_boundary_expr(exprP(i, j), cell(i - 1, j - 1), cell(i, j));
add_boundary_expr(exprQ(i, j), cell(i - 1, j), cell(i, j - 1));
}
}
for (int i = 1; i < r; i++) {
for (int j = 1; j < c; j++) {
auto [pv, pcst] = exprP(i, j);
auto [qv, qcst] = exprQ(i, j);
sat.add_or(pv, pcst ^ 1, qv, qcst ^ 1);
}
}
vector<int> val;
if (!sat.solve(val)) {
cout << "NO\n";
continue;
}
auto P = [&](int i, int j) {
auto [v, cs] = exprP(i, j);
return val[v] ^ cs;
};
auto Q = [&](int i, int j) {
auto [v, cs] = exprQ(i, j);
return val[v] ^ cs;
};
vector<string> x(r, string(c, '0'));
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
int bit;
if (i == 0 || j == 0) bit = P(i, j);
else bit = (x[i - 1][j - 1] - '0') ^ P(i, j);
x[i][j] = char('0' + bit);
}
}
auto get = [&](int i, int j) {
if (i < 0 || i >= r || j < 0 || j >= c) return 0;
return x[i][j] - '0';
};
bool ok = true;
for (int i = 0; i <= r && ok; i++) {
for (int j = 0; j <= c; j++) {
if ((get(i - 1, j - 1) ^ get(i, j)) != P(i, j)) ok = false;
if ((get(i - 1, j) ^ get(i, j - 1)) != Q(i, j)) ok = false;
}
}
if (!ok) {
cout << "NO\n";
continue;
}
cout << "YES\n";
for (int i = 0; i <= r; i++) {
string s;
s.reserve(c);
for (int j = 0; j < c; j++) {
s.push_back(char('0' + (get(i - 1, j) ^ get(i, j))));
}
cout << s << '\n';
}
for (int i = 0; i < r; i++) {
string s;
s.reserve(c + 1);
for (int j = 0; j <= c; j++) {
s.push_back(char('0' + (get(i, j - 1) ^ get(i, j))));
}
cout << s << '\n';
}
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct TwoSAT {
int n;
vector<vector<int>> g, rg;
TwoSAT(int n = 0) : n(n), g(2 * n), rg(2 * n) {}
int id(int v, int val) const { return 2 * v + val; }
void imply(int a, int b) {
g[a].push_back(b);
rg[b].push_back(a);
}
void add_or(int x, int xv, int y, int yv) {
imply(id(x, xv ^ 1), id(y, yv));
imply(id(y, yv ^ 1), id(x, xv));
}
void add_unit(int x, int xv) {
imply(id(x, xv ^ 1), id(x, xv));
}
void add_equal_expr(int x, int xc, int y, int yc) {
// (value[x] ^ xc) == (value[y] ^ yc)
add_or(x, xc, y, yc ^ 1);
add_or(y, yc, x, xc ^ 1);
}
bool solve(vector<int>& ans) {
int m = 2 * n;
vector<int> order, comp(m, -1), vis(m, 0);
order.reserve(m);
for (int s = 0; s < m; s++) if (!vis[s]) {
vector<pair<int, int>> st = {{s, 0}};
vis[s] = 1;
while (!st.empty()) {
int v = st.back().first;
int &it = st.back().second;
if (it < (int)g[v].size()) {
int to = g[v][it++];
if (!vis[to]) {
vis[to] = 1;
st.push_back({to, 0});
}
} else {
order.push_back(v);
st.pop_back();
}
}
}
int cc = 0;
for (int z = m - 1; z >= 0; z--) {
int s = order[z];
if (comp[s] != -1) continue;
vector<int> st = {s};
comp[s] = cc;
while (!st.empty()) {
int v = st.back();
st.pop_back();
for (int to : rg[v]) if (comp[to] == -1) {
comp[to] = cc;
st.push_back(to);
}
}
cc++;
}
ans.assign(n, 0);
for (int i = 0; i < n; i++) {
if (comp[2 * i] == comp[2 * i + 1]) return false;
ans[i] = comp[2 * i] < comp[2 * i + 1];
}
return true;
}
};
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int r, c;
cin >> r >> c;
vector<string> a(r);
for (string &s : a) cin >> s;
int diagCnt = r + c + 1;
int offQ = diagCnt;
int vars = 2 * diagCnt;
vector<vector<int>> pc(r + 1, vector<int>(c + 1, 0));
vector<vector<int>> qc(r + 1, vector<int>(c + 1, 0));
for (int s = 0; s <= r + c; s++) {
vector<pair<int, int>> pts;
for (int i = max(0, s - c); i <= min(r, s); i++) {
pts.push_back({i, s - i});
}
for (int t = 0; t + 1 < (int)pts.size(); t++) {
auto [i, j] = pts[t];
auto [ni, nj] = pts[t + 1];
pc[ni][nj] = pc[i][j] ^ (a[i][j - 1] == 'B');
}
}
for (int d = -r; d <= c; d++) {
vector<pair<int, int>> pts;
for (int i = max(0, -d); i <= min(r, c - d); i++) {
pts.push_back({i, i + d});
}
for (int t = 0; t + 1 < (int)pts.size(); t++) {
auto [i, j] = pts[t];
auto [ni, nj] = pts[t + 1];
qc[ni][nj] = qc[i][j] ^ (a[i][j] == 'B');
}
}
TwoSAT sat(vars);
auto exprP = [&](int i, int j) -> pair<int, int> {
return {i + j, pc[i][j]};
};
auto exprQ = [&](int i, int j) -> pair<int, int> {
return {offQ + (j - i + r), qc[i][j]};
};
auto cell = [&](int i, int j) -> int {
if (i < 0 || i >= r || j < 0 || j >= c) return -1;
return i * c + j;
};
vector<int> firstVar(r * c, -1), firstConst(r * c, 0);
auto add_boundary_expr = [&](pair<int, int> e, int u, int v) {
if (u == -1 && v == -1) {
sat.add_unit(e.first, e.second);
} else if ((u == -1) != (v == -1)) {
int id = (u == -1 ? v : u);
if (firstVar[id] == -1) {
firstVar[id] = e.first;
firstConst[id] = e.second;
} else {
sat.add_equal_expr(firstVar[id], firstConst[id], e.first, e.second);
}
}
};
for (int i = 0; i <= r; i++) {
for (int j = 0; j <= c; j++) {
add_boundary_expr(exprP(i, j), cell(i - 1, j - 1), cell(i, j));
add_boundary_expr(exprQ(i, j), cell(i - 1, j), cell(i, j - 1));
}
}
for (int i = 1; i < r; i++) {
for (int j = 1; j < c; j++) {
auto [pv, pcst] = exprP(i, j);
auto [qv, qcst] = exprQ(i, j);
sat.add_or(pv, pcst ^ 1, qv, qcst ^ 1);
}
}
vector<int> val;
if (!sat.solve(val)) {
cout << "NO\n";
continue;
}
auto P = [&](int i, int j) {
auto [v, cs] = exprP(i, j);
return val[v] ^ cs;
};
auto Q = [&](int i, int j) {
auto [v, cs] = exprQ(i, j);
return val[v] ^ cs;
};
vector<string> x(r, string(c, '0'));
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
int bit;
if (i == 0 || j == 0) bit = P(i, j);
else bit = (x[i - 1][j - 1] - '0') ^ P(i, j);
x[i][j] = char('0' + bit);
}
}
auto get = [&](int i, int j) {
if (i < 0 || i >= r || j < 0 || j >= c) return 0;
return x[i][j] - '0';
};
bool ok = true;
for (int i = 0; i <= r && ok; i++) {
for (int j = 0; j <= c; j++) {
if ((get(i - 1, j - 1) ^ get(i, j)) != P(i, j)) ok = false;
if ((get(i - 1, j) ^ get(i, j - 1)) != Q(i, j)) ok = false;
}
}
if (!ok) {
cout << "NO\n";
continue;
}
cout << "YES\n";
for (int i = 0; i <= r; i++) {
string s;
s.reserve(c);
for (int j = 0; j < c; j++) {
s.push_back(char('0' + (get(i - 1, j) ^ get(i, j))));
}
cout << s << '\n';
}
for (int i = 0; i < r; i++) {
string s;
s.reserve(c + 1);
for (int j = 0; j <= c; j++) {
s.push_back(char('0' + (get(i, j - 1) ^ get(i, j))));
}
cout << s << '\n';
}
}
}