#include #include using namespace std; using namespace atcoder; #define rep(i, l, r) for (ll i = (l); i < (r); ++i) #define all(x) (x).begin(), (x).end() using ll = long long; using pl = pair; using vl = vector; using vvl = vector>; using vvvl = vector>>; using vvvvl = vector>>>; #pragma GCC target("avx2") #pragma GCC optimize("O3") #pragma GCC optimize("unroll-loops") #include using mint=modint998244353; //グリッド構築24題対策ライブラリ using Board = std::vector>; // Rotations allowed; reflections forbidden. n,m must be positive. // Returns nullopt exactly when no tiling exists. // Time O(n*m), space O(n*m), including the returned board. inline std::optional tileTetromino(int n, int m, char type) { if (n <= 0 || m <= 0) throw std::invalid_argument("n and m must be positive"); if (std::string("STZIOLJ").find(type) == std::string::npos) throw std::invalid_argument("Unknown tetromino type"); const long long area = 1LL * n * m; bool possible = false; switch (type) { case 'I': possible = n % 4 == 0 || m % 4 == 0; break; case 'O': possible = n % 2 == 0 && m % 2 == 0; break; case 'T': possible = n % 4 == 0 && m % 4 == 0; break; case 'L': case 'J': possible = area % 8 == 0 && n != 1 && n != 3 && m != 1 && m != 3; break; case 'S': case 'Z': break; } if (!possible) return std::nullopt; if (area / 4 > std::numeric_limits::max()) throw std::length_error("Tile IDs do not fit in int"); int h = n, w = m; bool rotate = false; if (type == 'L' || type == 'J') { // Even case: w is divisible by 4. // Odd case: h is odd, w is divisible by 8. if ((h % 2 == 0 && w % 2 == 0 && w % 4 != 0) || (h % 2 == 0 && w % 2 != 0)) { std::swap(h, w); rotate = true; } } Board ans(n, std::vector(m)); int offset = 0; auto put = [&](int r, int c, const Board& p, int count) { for (int i = 0; i < static_cast(p.size()); ++i) { for (int j = 0; j < static_cast(p[i].size()); ++j) { // Rotate the entire virtual board by 90 degrees if needed. int rr = rotate ? c + j : r + i; int cc = rotate ? m - 1 - (r + i) : c + j; // Reflect an all-L tiling to obtain an all-J tiling. if (type == 'J') cc = m - 1 - cc; ans[rr][cc] = offset + p[i][j]; } } offset += count; }; if (type == 'I' || type == 'O' || type == 'T') { Board p; int count; if (type == 'I') { p = (m % 4 == 0) ? Board{{1, 1, 1, 1}} : Board{{1}, {1}, {1}, {1}}; count = 1; } else if (type == 'O') { p = {{1, 1}, {1, 1}}; count = 1; } else { p = {{1, 1, 1, 2}, {3, 1, 2, 2}, {3, 3, 4, 2}, {3, 4, 4, 4}}; count = 4; } int ph = static_cast(p.size()); int pw = static_cast(p[0].size()); for (int r = 0; r < h; r += ph) for (int c = 0; c < w; c += pw) put(r, c, p, count); } else { const Board p2 = {{1, 1, 1, 2}, {1, 2, 2, 2}}; const Board p5 = {{1, 1, 1, 5, 6, 2, 2, 2}, {1, 5, 5, 5, 6, 2, 3, 3}, {10, 9, 9, 9, 6, 6, 4, 3}, {10, 9, 8, 8, 8, 7, 4, 3}, {10, 10, 8, 7, 7, 7, 4, 4}}; int start = 0; if (h % 2 != 0) { for (int c = 0; c < w; c += 8) put(0, c, p5, 10); start = 5; } for (int r = start; r < h; r += 2) for (int c = 0; c < w; c += 4) put(r, c, p2, 2); } return ans; } // 回転・裏返しを許可する版。 // 前の tileTetromino が定義されていることが必要。 // 時間・空間計算量:O(n*m) inline std::optional tileTetrominoWithReflection( int n, int m, char type ) { // L・J以外、または辺の長さに3がない場合は前の関数でよい。 if ((type != 'L' && type != 'J') || (n != 3 && m != 3)) { return tileTetromino(n, m, type); } if (n <= 0 || m <= 0) { throw std::invalid_argument("n and m must be positive"); } // 3 × length の長方形として構成する。 const int length = (n == 3 ? m : n); if (length % 8 != 0) { return std::nullopt; } const int pattern[3][8] = { {1, 1, 2, 2, 2, 3, 4, 4}, {1, 5, 2, 3, 3, 3, 6, 4}, {1, 5, 5, 5, 6, 6, 6, 4} }; Board ans(n, std::vector(m)); int offset = 0; for (int start = 0; start < length; start += 8) { for (int r = 0; r < 3; ++r) { for (int c = 0; c < 8; ++c) { int id = offset + pattern[r][c]; if (n == 3) { ans[r][start + c] = id; } else { ans[start + c][r] = id; } } } offset += 6; } return ans; } int main(){ ios::sync_with_stdio(false); std::cin.tie(nullptr); ll tt; cin>>tt; rep(iaa,0,tt){ ll n,m; cin>>n>>m; auto u=tileTetromino(n,m,'L'); if(!u){ cout<<-1<