Problem:
問題 J
Mod 3 Knights Out
問題文
ある日の夕方，コンテストの問題を解き終えた二人は今日の問題について話し合っていた．
A「くそー，あのライツアウトの問題，mod
2
じゃなくて mod
3
とか mod
7
とかだったら解法分かったのにー！」
B「じゃあ，mod
3
で別の方法で解く問題を作ればいいんですね．分かりました！」
こうして次のような問題が誕生した．
H × W
のチェス盤がある．チェス盤の各マスには
0
から
2
の整数が書かれている．このチェス盤にナイトを置いていく．ただし，各マスには多くても
1
体のナイトしか置けない．各マスについて，
(マスの数値+そこを攻撃するマスにいるナイトの数)=0 mod 3
が成り立つようなナイトの配置を
良い
配置と呼ぶ．攻撃するマスとはそのマスから縦方向に
±2
マスかつ横方向に
±1
マス，もしくは縦方向に
±1
マスかつ横方向に
±2
マスずれたマスのことである．
良いナイトの配置の数を求めよ．答えは大きくなる可能性があるので
1,000,000,007
で割った余りを答えよ．
入力形式
最初の行に
H
と
W
がスペース区切りで与えられる．
次の
H
行には、チェス盤のマス目に書かれている数値として
W
個の
0
，
1
，
2
のいずれかの整数がスペース区切りで与えられる．
出力形式
良いナイトの配置の数を
1,000,000,007
で割った余りを出力せよ．
制約
1 ≤ H ≤ 50
1 ≤ W ≤ 16
入出力例
入力例 1
5 5
0 2 0 2 0
2 0 0 0 2
0 0 0 0 0
2 0 0 0 2
0 2 0 2 0
出力例 1
5
入力例 2
3 3
2 2 2
2 0 2
2 2 2
出力例 2
8
入力例 3
7 7
2 2 2 2 2 2 2
2 1 1 2 1 1 2
2 0 1 0 1 0 2
2 2 0 2 0 2 2
2 0 1 0 1 0 2
2 1 1 2 1 1 2
2 2 2 2 2 2 2
出力例 3
96
入力例 4
7 3
2 2 2
1 0 1
0 1 0
1 1 1
0 1 0
1 0 1
2 2 2
出力例 4
8
入力例 5
6 6
0 2 0 1 0 2
2 0 1 2 2 2
0 2 2 0 0 0
2 0 2 0 2 0
0 2 2 1 0 2
0 0 0 2 0 2
出力例 5
1
入力例 6
16 16
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
出力例 6
1
謝辞
この問題は Tester と Writer が
アジア地区予選の問題
に関して話し合ったのをきっかけとして作られた。

Submitted solutions:

code_snippet: // AOJ #2280 Mod 3 Knights Out
// 2025.4.30

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 1000000007;

#define gc() getchar_unlocked()

int Cin() {
	int n = 0; int c = gc();
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

const int dr[8]={-2,-2,-1,-1,1,1,2,2};
const int dc[8]={-1,1,-2,2,-2,2,-1,1};

inline int addmod(int a,int b){ a+=b; if(a>=MOD) a-=MOD; return a; }
inline int mulmod(ll a,ll b){ return (int)(a*b%MOD); }
ll powmod(ll a,ll e){
    ll r=1;
    while(e){
        if(e&1) r=r*a%MOD;
        a=a*a%MOD;
        e>>=1;
    }
    return r;
}
inline int inv3(int x){
    if(x==1) return 1;
    if(x==2) return 2;
    return 0;
}

int H,W;
vector<int> B;
vector<vector<int>> eq_to_var, var_to_eq;

ll solve_comp(const vector<int>& vars, const vector<int>& eqs){
    int n = vars.size(), m = eqs.size();
    if(m==0) return powmod(2, n);
    if(n==0){
        for(int e: eqs) if(B[e]!=0) return 0;
        return 1;
    }
    unordered_map<int,int> vid;
    vid.reserve(n*2);
    for(int i=0;i<n;i++) vid[vars[i]] = i;
    vector<vector<int>> A(m, vector<int>(n+1,0));
    for(int i=0;i<m;i++){
        int eq = eqs[i];
        A[i][n] = B[eq];
        for(int v: eq_to_var[eq]){
            auto it = vid.find(v);
            if(it != vid.end()) A[i][it->second] = (A[i][it->second] + 1) % 3;
        }
    }
    vector<int> where(n,-1);
    int row=0;
    for(int col=0;col<n && row<m;col++){
        int sel = row;
        while(sel<m && A[sel][col]==0) sel++;
        if(sel==m) continue;
        swap(A[sel], A[row]);
        int inv = inv3(A[row][col]);
        for(int j=col;j<=n;j++) A[row][j] = A[row][j]*inv%3;
        for(int i=0;i<m;i++) if(i!=row && A[i][col]){
            int f = A[i][col];
            for(int j=col;j<=n;j++){
                A[i][j] = (A[i][j] - f * A[row][j]) %3;
                if(A[i][j]<0) A[i][j]+=3;
            }
        }
        where[col] = row++;
    }
    for(int i=row;i<m;i++) if(A[i][n]!=0) return 0;
    vector<int> free_cols;
    for(int col=0;col<n;col++) if(where[col]==-1) free_cols.push_back(col);
    int k = free_cols.size();
    vector<int> x0(n,0);
    for(int col=0;col<n;col++){
        if(where[col]!=-1) x0[col] = A[ where[col] ][n];
    }
    vector<vector<int>> basis(k, vector<int>(n,0));
    for(int f=0;f<k;f++){
        int fc = free_cols[f];
        basis[f][fc] = 1;
        for(int col=0;col<n;col++){
            int r = where[col];
            if(r>=0){
                int v = A[r][fc];
                if(v) basis[f][col] = (3 - v) % 3;
            }
        }
    }
    vector<int> f_last(n, -1);
    for(int f=0; f<k; f++){
        for(int j=0;j<n;j++) if(basis[f][j]!=0) f_last[j] = f;
    }
    ll cnt = 0;
    vector<int> cur = x0;
    function<void(int)> dfs = [&](int f){
        if(f==k){
            for(int j=0;j<n;j++) if(cur[j]==2) return;
            cnt = (cnt + 1) % MOD;
            return;
        }
        for(int a=0;a<3;a++){
            if(a){
                for(int j=0;j<n;j++) cur[j] = (cur[j] + basis[f][j]*a) % 3;
            }
            bool ok = true;
            for(int j=0;j<n;j++){
                if(cur[j]==2 && f_last[j] <= f){ ok = false; break; }
            }
            if(ok) dfs(f+1);
            if(a){
                for(int j=0;j<n;j++){
                    cur[j] = (cur[j] - basis[f][j]*a) % 3;
                    if(cur[j]<0) cur[j]+=3;
                }
            }
        }
    };
    dfs(0);
    return cnt;
}

int main(){
    H = Cin(), W = Cin();
    int N = H*W;
    B.resize(N);
    for(int i=0;i<H;i++) for(int j=0;j<W;j++){
        int v = Cin();
        B[i*W+j] = ((-v)%3+3)%3;
    }
    eq_to_var.assign(N,{});
    var_to_eq.assign(N,{});
    for(int i=0;i<H;i++){
        for(int j=0;j<W;j++){
            int eq = i*W+j;
            for(int d=0;d<8;d++){
                int ni=i+dr[d], nj=j+dc[d];
                if(ni<0||ni>=H||nj<0||nj>=W) continue;
                int var = ni*W+nj;
                eq_to_var[eq].push_back(var);
                var_to_eq[var].push_back(eq);
            }
        }
    }
    vector<int> comp(2*N, -1);
    int comps = 0;
    for(int u=0; u<2*N; u++){
        if(comp[u]!=-1) continue;
        queue<int>q;
        comp[u]=comps;
        q.push(u);
        while(!q.empty()){
            int x=q.front(); q.pop();
            if(x< N){
                for(int e: var_to_eq[x]){
                    int y = e + N;
                    if(comp[y]==-1){
                        comp[y]=comps;
                        q.push(y);
                    }
                }
            } else {
                int e = x - N;
                for(int v: eq_to_var[e]){
                    if(comp[v]==-1){
                        comp[v]=comps;
                        q.push(v);
                    }
                }
            }
        }
        comps++;
    }
    vector<vector<int>> cv(comps), ce(comps);
    for(int v=0; v<N; v++) cv[ comp[v] ].push_back(v);
    for(int e=0; e<N; e++) ce[ comp[e+N] ].push_back(e);

    ll ans = 1;
    for(int c=0; c<comps; c++){
        ll sub = solve_comp(cv[c], ce[c]);
        if(sub==0){
            cout << 0 << endl;
            return 0;
        }
        ans = ans * sub % MOD;
    }
    cout << ans << endl;
    return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 4248
final_rank: 4

code_snippet: #include <bits/stdc++.h>

#define int long long
using namespace std;
const int mod = 1e9 + 7;
int h, w;
vector<vector<int> > g;
int delx[] = {-2, -1, 1, 2, -2, -1, 1, 2};
int dely[] = {-1, -2, -2, -1, 1, 2, 2, 1};

void add(int y, int x, int put) {
    for (int i = 0; i < 8; ++i) {
        int yy = y + dely[i];
        int xx = x + delx[i];
        if (xx >= 0 && xx < w && yy >= 0 && yy < h) {
            g[yy][xx] = (g[yy][xx] + put + 3) % 3;
        }
    }
}

bool check(int bw) {
    for (int i = h - 1; i >= max(0ll, h - 2); --i) {
        int st = ((i & 1) + 1 - bw) & 1;
        for (int j = st; j < w; j += 2) {
            if (g[i][j] != 0) {
                return false;
            }
        }
    }
    return true;
}

int rec(int y, int x, int bw) {
    if (y == h) {
        bool ok = check(bw);
        if (ok) {
            return 1;
        }
        return 0;
    }
    int nexty = y;
    int nextx = x + 2;
    int ret = 0;
    if (nextx >= w) {
        nexty = y + 1;
        nextx = ((nexty & 1) + bw) & 1;
    }
    bool put = true;
    bool notput = true;
    if (y > 1 && x > 0) {
        if (!g[y - 2][x - 1]) {
            put = false;
        } else if (g[y - 2][x - 1] == 1) {
            put = notput = false;
        } else {
            notput = false;
        }
    }
    if (y > 1 && x == w - 2) {
        if (!g[y - 2][x + 1]) {
            put = false;
        } else if (g[y - 2][x + 1] == 1) {
            put = notput = false;
        } else {
            notput = false;
        }
    }
    if (notput) {
        ret += rec(nexty, nextx, bw);
    }
    if (put) {
        add(y, x, 1);
        ret += rec(nexty, nextx, bw);
        add(y, x, -1);
    }
    return ret;
}

signed main() {
    // ios_base::sync_with_stdio(0);
    // cin.tie(0);
    cin >> h >> w;
    g.resize(h, vector<int>(w));
    for (int i = 0; i < h; ++i) {
        for (int j = 0; j < w; ++j) {
            cin >> g[i][j];
        }
    }
    if (h < w) {
        vector<vector<int> > gnew(w, vector<int>(h));
        for (int i = 0; i < h; ++i) {
            for (int j = 0; j < w; ++j) {
                gnew[j][i] = g[i][j];
            }
        }
        g = gnew;
        swap(h, w);
    }
    if (w == 1) {
        bool e = true;
        for (int i = 0; i < h; ++i) {
            if (g[i][0]) {
                e = false;
                break;
            }
        }
        if (!e) {
            printf("%d\n", 0);
        } else {
            printf("%d\n", (1ll << h) % mod);
        }
        return 0;
    }
    int black = rec(0, 0, 0);
    int white = rec(0, 1, 1);
    int ans = white * black % mod;
    printf("%d\n", ans);
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3312
final_rank: 2

code_snippet: #include <set>
#include <iostream>
#include <cstdio>
#include <cassert>
#define REP(i,n) for(int i=0; i<(int)(n); i++)

inline int getInt(){ int s; scanf("%d", &s); return s; }

#define IN(x,s,g) ((x) >= (s) && (x) < (g))
#define ISIN(x,y,w,h) (IN((x),0,(w)) && IN((y),0,(h)))

using namespace std;

const int mod = 1000000007;

int h, w;
int g[50][16];
int debug[50][16];

int dx[] = {-2, -1, 1, 2, -2, -1, 1, 2 };
int dy[] = {-1, -2, -2, -1, 1, 2, 2, 1 };

void debug_print(){
  REP(i, h){
    REP(j, w) printf("%d ", debug[i][j]);
    printf("   ");
    REP(j, w) printf("%d ", g[i][j]);
    puts("");
  }
}

void process(int y, int x, bool put){
  int p = put ? 1 : 2;

  // debug[y][x] = put ? 1 : 0;

  REP(i,8){
    int yy = y + dy[i];
    int xx = x + dx[i];

    if(ISIN(xx, yy, w, h)){
      g[yy][xx] = (g[yy][xx] + p) % 3;
    }
  }
}

bool check(int bw){
  REP(i,2) if(h - 1 - i >= 0){
    int st = ((h - 1 - i) % 2 + (bw ^ 1)) % 2;
    for(int j = st; j < w; j += 2){
      if(g[h - 1 - i][j] != 0) return false;
    }
  }
  return true;
}

int solve(int y, int x, int bw){
  if(y == h){
    bool ok = check(bw);
    // printf("debug(%d): %d\n", bw, ok); debug_print(); puts("");
    return ok ? 1 : 0;
  }

  assert((x + y) % 2 == bw);

  int yy = y;
  int xx = x + 2;

  int ret = 0;

  if(xx >= w){
    yy = y + 1;
    xx = (yy % 2 + bw) % 2;
  }

  bool put    = true;
  bool notput = true;

  if(y > 1 && x > 0){
    if(g[y-2][x-1] == 0){
      put = false;
    }else if(g[y-2][x-1] == 1){
      put = notput = false;
    }else{
      notput = false;
    }
  }

  if(y > 1 && x == w - 2){
    if(g[y-2][x+1] == 0){
      put = false;
    }else if(g[y-2][x+1] == 1){
      put = notput = false;
    }else{
      notput = false;
    }
  }

  if(notput){
    ret += solve(yy, xx, bw); ret %= mod;
  }
  if(put){
    process(y, x, true);
    ret += solve(yy, xx, bw); ret %= mod;
    process(y, x, false);
  }

  return ret;
}

int main(){
  h = getInt();
  w = getInt();

  REP(i,h) REP(j,w)
    g[i][j] = getInt();

  if(h < w){
    REP(i,w) REP(j,i)
      swap(g[i][j], g[j][i]);
    swap(h, w);
  }


  if(w == 1){
    bool ok = true;
    REP(i,h) if(g[i][0] != 0) ok = false;
    if(ok){
      int ans = 1;
      REP(i,h) ans = (ans * 2) % mod;
      printf("%d\n", ans);
    }else{
      puts("0");
    }
    return 0;
  }

  int black = solve(0, 0, 0);
  int white = (w == 1 ? solve(1, 0, 1) : solve(0, 1, 1));
  int ans   = (((long long)white) * black) % mod;

  printf("%d\n", ans);

  return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3240
final_rank: 1

code_snippet: #include<cstdio>

#define rep(i,n) for(int i=0;i<(n);i++)

using namespace std;

typedef long long ll;

const ll M=1000000007;
const int dx[]={2,1,-1,-2,-2,-1,1,2},dy[]={-1,-2,-2,-1,1,2,2,1};

int h,w,B[50][16];

int cnt[50][16]; // 攻撃されうる回数

ll dfs(int i,int j,int parity){
	if(i==h) return 1;
	if(j==w) return dfs(i+1,0,parity);

	if((i+j)%2!=parity) return dfs(i,j+1,parity); // 黒マス or 白マスのみ見る

	// マス (i, j) での値を mod 3 で 0 にできなければ探索打ち切り ( (i, j) を攻撃するマスがないかもしれないので必要 )
	if(cnt[i][j]==0 && B[i][j]%3!=0
	|| cnt[i][j]==1 && B[i][j]%3==1) return 0;

	ll res=0;
	// マス (i, j) にナイトを置かない
	rep(k,8){
		int y=i+dy[k],x=j+dx[k];
		if(0<=y && y<h && 0<=x && x<w) cnt[y][x]--;
	}
	bool ok=true;
	rep(k,8){
		int y=i+dy[k],x=j+dx[k];
		if(0<=y && y<h && 0<=x && x<w){
			if(cnt[y][x]==0 && B[y][x]%3!=0
			|| cnt[y][x]==1 && B[y][x]%3==1) ok=false;
		}
	}
	if(ok) res+=dfs(i,j+1,parity);

	// マス (i, j) にナイトを置く
	rep(k,8){
		int y=i+dy[k],x=j+dx[k];
		if(0<=y && y<h && 0<=x && x<w) B[y][x]++;
	}
	ok=true;
	rep(k,8){
		int y=i+dy[k],x=j+dx[k];
		if(0<=y && y<h && 0<=x && x<w){
			if(cnt[y][x]==0 && B[y][x]%3!=0
			|| cnt[y][x]==1 && B[y][x]%3==1) ok=false;
		}
	}
	if(ok) res+=dfs(i,j+1,parity);

	rep(k,8){
		int y=i+dy[k],x=j+dx[k];
		if(0<=y && y<h && 0<=x && x<w) cnt[y][x]++, B[y][x]--;
	}

	return res;
}

int main(){
	scanf("%d%d",&h,&w);
	rep(i,h) rep(j,w) scanf("%d",B[i]+j);

	rep(i,h) rep(j,w) rep(k,8) {
		int y=i+dy[k],x=j+dx[k];
		if(0<=y && y<h && 0<=x && x<w) cnt[y][x]++;
	}

	ll ans;
	if(h==1 || w==1){ // コーナーケース
		bool ok=true;
		rep(i,h) rep(j,w) if(B[i][j]!=0) ok=false;
		if(ok){
			ans=1;
			rep(i,h*w) ans=2*ans%M;
		}
		else ans=0;
	}
	else{
		ans=dfs(0,0,0)*dfs(0,1,1)%M;
	}
	printf("%lld\n",ans);

	return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 748
final_rank: 3

code_snippet: #include<cstdio>

#define rep(i,n) for(int i=0;i<(n);i++)

using namespace std;

typedef long long ll;

const ll M=1000000007;
const int dx[]={2,1,-1,-2,-2,-1,1,2},dy[]={-1,-2,-2,-1,1,2,2,1};

int h,w,B[50][16];

int cnt[50][16]; // 攻撃されうる回数

ll dfs(int i,int j,int parity){
	if(i==h) return 1;
	if(j==w) return dfs(i+1,0,parity);

	if((i+j)%2!=parity) return dfs(i,j+1,parity); // 黒マス or 白マスのみ見る

	ll res=0;
	// マス (i, j) にナイトを置く
	rep(k,8){
		int y=i+dy[k],x=j+dx[k];
		if(0<=y && y<h && 0<=x && x<w) cnt[y][x]--;
	}
	bool ok=true;
	rep(k,8){
		int y=i+dy[k],x=j+dx[k];
		if(0<=y && y<h && 0<=x && x<w){
			if(cnt[y][x]==0 && B[y][x]%3!=0
			|| cnt[y][x]==1 && B[y][x]%3==1) ok=false;
		}
	}
	if(ok) res+=dfs(i,j+1,parity);

	// マス (i, j) にナイトを置かない
	rep(k,8){
		int y=i+dy[k],x=j+dx[k];
		if(0<=y && y<h && 0<=x && x<w) B[y][x]++;
	}
	ok=true;
	rep(k,8){
		int y=i+dy[k],x=j+dx[k];
		if(0<=y && y<h && 0<=x && x<w){
			if(cnt[y][x]==0 && B[y][x]%3!=0
			|| cnt[y][x]==1 && B[y][x]%3==1) ok=false;
		}
	}
	if(ok) res+=dfs(i,j+1,parity);

	rep(k,8){
		int y=i+dy[k],x=j+dx[k];
		if(0<=y && y<h && 0<=x && x<w) cnt[y][x]++, B[y][x]--;
	}

	return res;
}

int main(){
	scanf("%d%d",&h,&w);
	rep(i,h) rep(j,w) scanf("%d",B[i]+j);

	rep(i,h) rep(j,w) cnt[i][j]=0;
	rep(i,h) rep(j,w) rep(k,8) {
		int y=i+dy[k],x=j+dx[k];
		if(0<=y && y<h && 0<=x && x<w) cnt[y][x]++;
	}

	ll ans;
	if(h==1 || w==1){
		ans=1;
		rep(i,h*w) ans=2*ans%M;
	}
	else ans=dfs(0,0,0)*dfs(0,1,1)%M;
	printf("%lld\n",ans);

	return 0;
}
accuracy: 0.21875
time_ms: 60
memory_kb: 748
final_rank: 5