| #include <bits/stdc++.h> |
| using namespace std; |
|
|
| static constexpr int K_MAX = 240; |
|
|
| |
| static constexpr int MIN_SIDE = 15; |
| static constexpr int MARGIN = 2; |
| static constexpr int SPACING = 2; |
|
|
| struct NodeInfo { |
| int w = MIN_SIDE, h = MIN_SIDE; |
| int cols = 0, rows = 0; |
| int maxw = 0, maxh = 0; |
| }; |
|
|
| struct Rect { |
| int r0 = 0, c0 = 0, h = 0, w = 0; |
| }; |
|
|
| static inline int isqrt_ceil(int x) { |
| int r = (int)std::sqrt((double)x); |
| while (r * r < x) ++r; |
| return r; |
| } |
|
|
| static inline bool inside(int r, int c, int K) { |
| return (0 <= r && r < K && 0 <= c && c < K); |
| } |
|
|
| static inline bool is_interior_same(const vector<vector<int>> &grid, int r, int c, int color) { |
| return grid[r][c] == color && |
| grid[r-1][c] == color && |
| grid[r+1][c] == color && |
| grid[r][c-1] == color && |
| grid[r][c+1] == color; |
| } |
|
|
| vector<vector<int>> create_map(int N, int M, vector<int> A, vector<int> B) { |
| if (N <= 0) return {}; |
|
|
| vector<vector<int>> adjList(N + 1); |
| vector<vector<char>> adjMat(N + 1, vector<char>(N + 1, 0)); |
| vector<pair<int,int>> edges; |
| edges.reserve(M); |
|
|
| for (int i = 0; i < M; i++) { |
| int u = A[i], v = B[i]; |
| edges.push_back({u, v}); |
| adjList[u].push_back(v); |
| adjList[v].push_back(u); |
| adjMat[u][v] = adjMat[v][u] = 1; |
| } |
|
|
| int root = 1; |
|
|
| |
| vector<int> parent(N + 1, -1); |
| parent[root] = 0; |
| queue<int> q; |
| q.push(root); |
| while (!q.empty()) { |
| int u = q.front(); q.pop(); |
| for (int v : adjList[u]) { |
| if (parent[v] == -1) { |
| parent[v] = u; |
| q.push(v); |
| } |
| } |
| } |
|
|
| |
| |
| for (int v = 1; v <= N; v++) { |
| if (parent[v] == -1) { |
| |
| int attach = -1; |
| for (int u = 1; u <= N; u++) { |
| if (parent[u] != -1 && adjMat[u][v]) { attach = u; break; } |
| } |
| if (attach == -1) { |
| |
| attach = root; |
| } |
| parent[v] = attach; |
| } |
| } |
|
|
| vector<vector<int>> children(N + 1); |
| for (int v = 1; v <= N; v++) children[v].clear(); |
| for (int v = 1; v <= N; v++) { |
| if (v == root) continue; |
| children[parent[v]].push_back(v); |
| } |
|
|
| |
| vector<NodeInfo> info(N + 1); |
|
|
| function<void(int)> dfs_size = [&](int u) { |
| for (int c : children[u]) dfs_size(c); |
| int k = (int)children[u].size(); |
| if (k == 0) { |
| info[u].w = info[u].h = MIN_SIDE; |
| info[u].cols = info[u].rows = 0; |
| info[u].maxw = info[u].maxh = 0; |
| return; |
| } |
| int maxw = 0, maxh = 0; |
| for (int c : children[u]) { |
| maxw = max(maxw, info[c].w); |
| maxh = max(maxh, info[c].h); |
| } |
| int cols = isqrt_ceil(k); |
| int rows = (k + cols - 1) / cols; |
|
|
| int w = max(MIN_SIDE, 2 * MARGIN + cols * maxw + (cols - 1) * SPACING); |
| int h = max(MIN_SIDE, 2 * MARGIN + rows * maxh + (rows - 1) * SPACING); |
|
|
| info[u].w = w; |
| info[u].h = h; |
| info[u].cols = cols; |
| info[u].rows = rows; |
| info[u].maxw = maxw; |
| info[u].maxh = maxh; |
| }; |
|
|
| dfs_size(root); |
|
|
| int K = max({6 * N, info[root].w, info[root].h}); |
| if (K > K_MAX) K = K_MAX; |
| if (K < 3) K = 3; |
|
|
| |
| vector<Rect> rect(N + 1); |
| rect[root] = {0, 0, K, K}; |
|
|
| function<void(int)> dfs_place = [&](int u) { |
| int k = (int)children[u].size(); |
| if (k == 0) return; |
|
|
| int cols = max(1, info[u].cols); |
| int maxw = info[u].maxw; |
| int maxh = info[u].maxh; |
|
|
| for (int idx = 0; idx < k; idx++) { |
| int c = children[u][idx]; |
| int rr = idx / cols; |
| int cc = idx % cols; |
|
|
| Rect rc; |
| rc.r0 = rect[u].r0 + MARGIN + rr * (maxh + SPACING); |
| rc.c0 = rect[u].c0 + MARGIN + cc * (maxw + SPACING); |
| rc.h = info[c].h; |
| rc.w = info[c].w; |
|
|
| |
| int pr1 = rect[u].r0 + rect[u].h - MARGIN; |
| int pc1 = rect[u].c0 + rect[u].w - MARGIN; |
| if (rc.r0 + rc.h > pr1) rc.r0 = max(rect[u].r0 + MARGIN, pr1 - rc.h); |
| if (rc.c0 + rc.w > pc1) rc.c0 = max(rect[u].c0 + MARGIN, pc1 - rc.w); |
|
|
| rect[c] = rc; |
| dfs_place(c); |
| } |
| }; |
|
|
| dfs_place(root); |
|
|
| |
| vector<vector<int>> grid(K, vector<int>(K, root)); |
|
|
| function<void(int)> dfs_paint = [&](int u) { |
| if (u != root) { |
| const Rect &R = rect[u]; |
| for (int r = R.r0; r < R.r0 + R.h; r++) { |
| for (int c = R.c0; c < R.c0 + R.w; c++) { |
| grid[r][c] = u; |
| } |
| } |
| } |
| for (int c : children[u]) dfs_paint(c); |
| }; |
|
|
| dfs_paint(root); |
|
|
| |
| vector<vector<char>> inTree(N + 1, vector<char>(N + 1, 0)); |
| for (int v = 1; v <= N; v++) { |
| if (v == root) continue; |
| int p = parent[v]; |
| inTree[v][p] = inTree[p][v] = 1; |
| } |
|
|
| |
| vector<vector<pair<int,int>>> candidates(N + 1); |
| candidates.assign(N + 1, {}); |
| for (int r = 1; r + 1 < K; r++) { |
| for (int c = 1; c + 1 < K; c++) { |
| int col = grid[r][c]; |
| if (col >= 1 && col <= N && is_interior_same(grid, r, c, col)) { |
| candidates[col].push_back({r, c}); |
| } |
| } |
| } |
|
|
| vector<vector<unsigned char>> blocked(K, vector<unsigned char>(K, 0)); |
| vector<int> usedCnt(N + 1, 0); |
|
|
| auto block_cell = [&](int r, int c) { |
| if (inside(r, c, K)) blocked[r][c] = 1; |
| }; |
|
|
| auto get_spot = [&](int container) -> pair<int,int> { |
| auto &vec = candidates[container]; |
| while (!vec.empty()) { |
| auto [r, c] = vec.back(); |
| vec.pop_back(); |
| if (blocked[r][c]) continue; |
| if (r <= 0 || r + 1 >= K || c <= 0 || c + 1 >= K) continue; |
| if (!is_interior_same(grid, r, c, container)) continue; |
| return {r, c}; |
| } |
| return {-1, -1}; |
| }; |
|
|
| auto place_island = [&](int container, int other) -> bool { |
| auto [r, c] = get_spot(container); |
| if (r < 0) return false; |
|
|
| |
| grid[r][c] = other; |
|
|
| |
| block_cell(r, c); |
| block_cell(r-1, c); |
| block_cell(r+1, c); |
| block_cell(r, c-1); |
| block_cell(r, c+1); |
|
|
| usedCnt[container]++; |
| return true; |
| }; |
|
|
| auto brute_place_island = [&](int container, int other) -> bool { |
| for (int r = 1; r + 1 < K; r++) { |
| for (int c = 1; c + 1 < K; c++) { |
| if (blocked[r][c]) continue; |
| if (is_interior_same(grid, r, c, container)) { |
| grid[r][c] = other; |
| block_cell(r, c); |
| block_cell(r-1, c); |
| block_cell(r+1, c); |
| block_cell(r, c-1); |
| block_cell(r, c+1); |
| usedCnt[container]++; |
| return true; |
| } |
| } |
| } |
| return false; |
| }; |
|
|
| |
| for (auto [u, v] : edges) { |
| if (inTree[u][v]) continue; |
|
|
| |
| long long estU = (long long)candidates[u].size() - 5LL * usedCnt[u]; |
| long long estV = (long long)candidates[v].size() - 5LL * usedCnt[v]; |
|
|
| int container = u, other = v; |
| if (estV > estU) { container = v; other = u; } |
|
|
| bool ok = place_island(container, other); |
| if (!ok) ok = place_island(other, container); |
| if (!ok) ok = brute_place_island(container, other); |
| if (!ok) ok = brute_place_island(other, container); |
|
|
| |
| } |
|
|
| return grid; |
| } |
|
|
| int main() { |
| ios::sync_with_stdio(false); |
| cin.tie(nullptr); |
|
|
| int T; |
| if (!(cin >> T)) return 0; |
| for (int tc = 0; tc < T; tc++) { |
| int N, M; |
| cin >> N >> M; |
| vector<int> A(M), B(M); |
| for (int i = 0; i < M; i++) { |
| cin >> A[i] >> B[i]; |
| } |
|
|
| auto C = create_map(N, M, A, B); |
| int K = (int)C.size(); |
|
|
| cout << K << "\n"; |
| for (int i = 0; i < K; i++) { |
| if (i) cout << ' '; |
| cout << K; |
| } |
| cout << "\n"; |
| for (int r = 0; r < K; r++) { |
| for (int c = 0; c < K; c++) { |
| if (c) cout << ' '; |
| cout << C[r][c]; |
| } |
| cout << "\n"; |
| } |
| if (tc + 1 < T) cout << "\n"; |
| } |
| return 0; |
| } |