Invert a Ternary Neighbour-Sum Grid
Autumn Tournament (2008)
Submission name: table.cpp
Problem summary
A grid S has entries0,1,2. T[i][j] is the sum modulo3 of S at its orthogonal neighbours. Given T, count all possible S modulo333333. Cells outside the grid contribute0.
Input
m n, then the m by n grid T.
Output
The number of compatible grids, modulo333333.
Constraints
2≤m,n≤32.
Worked example
Original statement sample.
Input
3 4 0 2 2 1 1 0 2 2 1 1 0 2
Output
1
Solution analysis
- Choosing the first row of S determines every later row. If the row above and the current row are known, rearrange the neighbour equation to obtain S[i+1][j]=T[i][j]−S[i−1][j]−S[i][j−1]−S[i][j+1] modulo3.
- Treat the n first-row cells as variables. Carry each derived cell as an affine expression with n coefficients and a constant. Propagate through all m rows. The extra row m+1 must be zero, leaving n linear equations in n unknowns. Solve those equations over the field modulo3, not modulo333333.
- For the sample, write the first row as x1,x2,x3,x4. The next row starts with −x2 and2−x1−x3. Continuing propagation and imposing the zero bottom row gives rank4, so the four first-row values are uniquely determined and the answer is1. In general, an inconsistent system contributes0; otherwise each free variable has three choices.
Why it works
The recurrence is a rearrangement of exactly one cell equation, so it uniquely constructs all remaining rows from the first row. All equations except the bottom boundary are enforced during construction. The final linear system enforces that boundary too, establishing a bijection between its solutions and valid grids. A consistent rank-r system over F3 has3^(n−r) solutions.
Watch out. Do not eliminate modulo333333: it is not a field. Equations use modulo3; only the final count uses333333. Include absent left/right/top/bottom neighbours as zero and normalize negative remainders.
Commented C++17 solution
An independently written complete program with standard headers.
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
int mod3(int value){
value%=3;
if(value<0){
value+=3;
}
return value;
}
int main(){
int rows,columns;
cin>>rows>>columns;
vector<vector<int>>target(rows,vector<int>(columns));
for(auto&row:target){
for(int&value:row){
cin>>value;
}
}
using Row=vector<vector<int>>;
Row above(columns,vector<int>(columns+1,0));
Row current=above;
for(int j=0;j<columns;j++){
current[j][j]=1;
}
for(int i=0;i<rows;i++){
Row below(columns,vector<int>(columns+1,0));
for(int j=0;j<columns;j++){
for(int coefficient=0;coefficient<=columns;coefficient++){
int value=-above[j][coefficient];
if(j>0){
value-=current[j-1][coefficient];
}
if(j+1<columns){
value-=current[j+1][coefficient];
}
if(coefficient==columns){
value+=target[i][j];
}
below[j][coefficient]=mod3(value);
}
}
above.swap(current);
current.swap(below);
}
// The propagated extra row must equal zero.
for(auto&equation:current){
equation[columns]=mod3(-equation[columns]);
}
int rank=0;
for(int column=0;column<columns;column++){
int pivot=rank;
while(pivot<columns&¤t[pivot][column]==0){
pivot++;
}
if(pivot==columns){
continue;
}
swap(current[pivot],current[rank]);
int inverse=current[rank][column];
for(int j=column;j<=columns;j++){
current[rank][j]=mod3(current[rank][j]*inverse);
}
for(int row=rank+1;row<columns;row++){
int factor=current[row][column];
for(int j=column;j<=columns;j++){
current[row][j]=mod3(current[row][j]-factor*current[rank][j]);
}
}
rank++;
}
for(int row=rank;row<columns;row++){
if(current[row][columns]!=0){
cout<<0<<'\n';
return 0;
}
}
int answer=1;
for(int freeVariable=rank;freeVariable<columns;freeVariable++){
answer=answer*3%333333;
}
cout<<answer<<'\n';
}