#include using namespace std; struct GadgetCell { int row, col; // position relative to gadget area top-left int color; // which gadget endpoint }; struct Layout { int w = 0, h = 0; int innerW = 0, innerH = 0; int childrenH = 0; int sep = 0; int gadgetW = 0, gadgetH = 0; int gadgetStartY = 0; int gadgetPlacement = 0; // 0=below, 1=right int gadgetX = 0, gadgetY = 0; // relative position of gadget block in inner area vector order; vector> pos; vector gadgetCells; }; // Pack children with adjacency-aware gaps // Children that are adjacent in the original graph can be placed without gaps static int pack_children_height_adj(const vector& order, const vector& lay, int innerW, bool isEdge[41][41], vector>* outPos = nullptr) { if (order.empty()) return 0; if (outPos) { outPos->clear(); outPos->reserve(order.size()); } int x = 0, y = 0, rowH = 0; int prevV = -1; for (int v : order) { int wv = lay[v].w; int hv = lay[v].h; // Determine gap: 0 if previous child is adjacent, 1 otherwise int gap = 1; // default gap if (prevV != -1 && isEdge[prevV][v]) { gap = 0; // adjacent children can touch } if (x > 0 && x + gap + wv > innerW) { y += rowH + 1; // 1-row gap between shelves (always needed since different shelves might not be adjacent) x = 0; rowH = 0; prevV = -1; gap = 0; // first on new row } if (x > 0) x += gap; if (outPos) outPos->push_back({y, x}); x += wv; rowH = max(rowH, hv); prevV = v; } return y + rowH; } struct Builder { int N = 0, M = 0; vector A, B; vector> adj; bool isEdge[41][41]{}; bool isTree[41][41]{}; vector parent; vector> children; vector> gadgets; vector lay; int root = 1; // mode: 0=BFS, 1=DFS bool build_spanning_tree_from(int r, int mode = 0) { root = r; parent.assign(N + 1, -1); children.assign(N + 1, {}); memset(isTree, 0, sizeof(isTree)); parent[r] = 0; if (mode == 0) { queue q; q.push(r); while (!q.empty()) { int u = q.front(); q.pop(); for (int v : adj[u]) { if (parent[v] == -1) { parent[v] = u; q.push(v); } } } } else { stack st; st.push(r); while (!st.empty()) { int u = st.top(); st.pop(); for (int v : adj[u]) { if (parent[v] == -1) { parent[v] = u; st.push(v); } } } } for (int i = 1; i <= N; i++) { if (parent[i] == -1) parent[i] = r; } for (int i = 1; i <= N; i++) { if (i == r) continue; int p = parent[i]; isTree[min(i,p)][max(i,p)] = true; children[p].push_back(i); } return true; } void assign_gadgets() { gadgets.assign(N + 1, {}); vector load(N + 1, 0); for (int i = 0; i < M; i++) { int a = A[i], b = B[i]; int x = min(a,b), y = max(a,b); if (isTree[x][y]) continue; // Also check if both are children of the same parent - if so, their borders touching // already creates the edge (if we place them without gap) // This is handled by placing adjacent siblings together int u; if (load[a] < load[b]) u = a; else if (load[b] < load[a]) u = b; else u = min(a,b); int v = (u == a ? b : a); gadgets[u].push_back(v); load[u]++; } } void compute_gadget_layout(int u, int& gW, int& gH, vector& cells) { int g = (int)gadgets[u].size(); cells.clear(); if (g == 0) { gW = 0; gH = 0; return; } // Try different arrangements struct Candidate { int w, h; vector cells; }; vector candidates; // 1. Checkerboard layouts for (int nRows = 1; nRows <= g; nRows++) { int cols = (g + nRows - 1) / nRows; int gw = 2 * cols - 1; int gh = 2 * nRows - 1; Candidate c; c.w = gw; c.h = gh; for (int idx = 0; idx < g; idx++) { int rr = (idx / cols) * 2; int cc = (idx % cols) * 2; c.cells.push_back({rr, cc, gadgets[u][idx]}); } candidates.push_back(c); } // 2. Single-row adjacency-aware layout { // Order gadgets to maximize consecutive adjacencies vector gOrder; { vector used(g, false); // Start from gadget with fewest adj int bestStart = 0; int minAdj = g; for (int i = 0; i < g; i++) { int ac = 0; for (int j = 0; j < g; j++) { if (i != j && isEdge[gadgets[u][i]][gadgets[u][j]]) ac++; } if (ac < minAdj) { minAdj = ac; bestStart = i; } } gOrder.push_back(bestStart); used[bestStart] = true; while ((int)gOrder.size() < g) { int last = gOrder.back(); int bestNext = -1, bestDeg = g+1; for (int i = 0; i < g; i++) { if (!used[i] && isEdge[gadgets[u][last]][gadgets[u][i]]) { int deg = 0; for (int j = 0; j < g; j++) { if (!used[j] && j != i && isEdge[gadgets[u][i]][gadgets[u][j]]) deg++; } if (bestNext == -1 || deg < bestDeg) { bestNext = i; bestDeg = deg; } } } if (bestNext == -1) { for (int i = 0; i < g; i++) { if (!used[i]) { bestNext = i; break; } } } gOrder.push_back(bestNext); used[bestNext] = true; } } // Single row { Candidate c; c.h = 1; int x = 0; for (int i = 0; i < g; i++) { c.cells.push_back({0, x, gadgets[u][gOrder[i]]}); if (i + 1 < g) { if (isEdge[gadgets[u][gOrder[i]]][gadgets[u][gOrder[i+1]]]) x += 1; else x += 2; } } c.w = x + 1; candidates.push_back(c); } // Multi-row adjacency-aware for (int nRows = 2; nRows <= g; nRows++) { int rowSize = (g + nRows - 1) / nRows; Candidate c; c.h = 2 * nRows - 1; int maxW = 0; for (int r = 0; r < nRows; r++) { int start = r * rowSize; int end = min(start + rowSize, g); int x = 0; for (int i = start; i < end; i++) { c.cells.push_back({2*r, x, gadgets[u][gOrder[i]]}); if (i + 1 < end) { if (isEdge[gadgets[u][gOrder[i]]][gadgets[u][gOrder[i+1]]]) x += 1; else x += 2; } } maxW = max(maxW, x + 1); } c.w = maxW; candidates.push_back(c); } } // Pick best candidate int bestIdx = 0; long long bestMetric = (1LL << 60); for (int i = 0; i < (int)candidates.size(); i++) { auto& c = candidates[i]; int maxDim = max(c.w, c.h); long long area = (long long)c.w * c.h; long long metric = (long long)maxDim * 1000000LL + area; if (metric < bestMetric) { bestMetric = metric; bestIdx = i; } } gW = candidates[bestIdx].w; gH = candidates[bestIdx].h; cells = candidates[bestIdx].cells; } // Try multiple child orderings to find the best packing vector best_child_order(int u, const vector& childrenList) { if (childrenList.size() <= 1) return childrenList; // Try: sort by width desc, height desc (original) vector> orders; // Order 1: width desc { vector o = childrenList; sort(o.begin(), o.end(), [&](int a, int b) { if (lay[a].w != lay[b].w) return lay[a].w > lay[b].w; return lay[a].h > lay[b].h; }); orders.push_back(o); } // Order 2: height desc { vector o = childrenList; sort(o.begin(), o.end(), [&](int a, int b) { if (lay[a].h != lay[b].h) return lay[a].h > lay[b].h; return lay[a].w > lay[b].w; }); orders.push_back(o); } // Order 3: area desc { vector o = childrenList; sort(o.begin(), o.end(), [&](int a, int b) { return lay[a].w * lay[a].h > lay[b].w * lay[b].h; }); orders.push_back(o); } // Order 4: Try to group adjacent children together (greedy) { vector o; vector used(N + 1, false); // Start with largest child vector sorted = childrenList; sort(sorted.begin(), sorted.end(), [&](int a, int b) { return lay[a].w * lay[a].h > lay[b].w * lay[b].h; }); o.push_back(sorted[0]); used[sorted[0]] = true; while ((int)o.size() < (int)childrenList.size()) { int last = o.back(); int bestNext = -1; // Prefer adjacent children for (int v : sorted) { if (!used[v] && isEdge[last][v]) { bestNext = v; break; } } if (bestNext == -1) { for (int v : sorted) { if (!used[v]) { bestNext = v; break; } } } o.push_back(bestNext); used[bestNext] = true; } orders.push_back(o); } return orders[0]; // For now, return first; we'll evaluate in dfs_size } bool dfs_size(int u, int Kmax, bool isRoot = false) { for (int v : children[u]) { if (!dfs_size(v, Kmax)) return false; } // Leaf with no gadgets: minimal 1x1 representation // The parent's border will create the tree edge if (children[u].empty() && gadgets[u].empty() && !isRoot) { Layout L; L.w = 1; L.h = 1; L.innerW = 1; L.innerH = 1; L.gadgetW = 0; L.gadgetH = 0; lay[u] = std::move(L); return true; } // Root can use border=0 to save space, but only if root color will still appear // Root color appears in: gap cells between children, gadget area, separators // Safe if: >= 2 children (gaps exist) or gadgets exist bool canSkipBorder = isRoot && (children[u].size() >= 2 || !gadgets[u].empty()); int border = canSkipBorder ? 0 : 1; Layout L; int gW, gH; vector gCells; compute_gadget_layout(u, gW, gH, gCells); L.gadgetW = gW; L.gadgetH = gH; L.gadgetCells = gCells; // Try multiple child orderings vector> orders; { vector o = children[u]; sort(o.begin(), o.end(), [&](int a, int b) { if (lay[a].w != lay[b].w) return lay[a].w > lay[b].w; return lay[a].h > lay[b].h; }); orders.push_back(o); } { vector o = children[u]; sort(o.begin(), o.end(), [&](int a, int b) { if (lay[a].h != lay[b].h) return lay[a].h > lay[b].h; return lay[a].w > lay[b].w; }); orders.push_back(o); } // Greedy adjacency grouping order if (children[u].size() > 1) { vector o; vector used(N + 1, false); vector sorted = children[u]; sort(sorted.begin(), sorted.end(), [&](int a, int b) { return lay[a].w * lay[a].h > lay[b].w * lay[b].h; }); o.push_back(sorted[0]); used[sorted[0]] = true; while ((int)o.size() < (int)children[u].size()) { int last = o.back(); int bestNext = -1; for (int v : sorted) { if (!used[v] && isEdge[last][v]) { bestNext = v; break; } } if (bestNext == -1) { for (int v : sorted) { if (!used[v]) { bestNext = v; break; } } } o.push_back(bestNext); used[bestNext] = true; } orders.push_back(o); } int bestInnerW = -1; int bestInnerH = -1; int bestChildrenH = -1; int bestSep = 0; int bestGadgetPlacement = 0; long long bestMetric = (1LL << 60); vector bestOrder; for (auto& order : orders) { int maxChildW = 0; for (int v : order) maxChildW = max(maxChildW, lay[v].w); // Try gadget below children { int minInnerW = max(1, max(maxChildW, gW)); for (int innerW = minInnerW; innerW <= Kmax - 2*border; innerW++) { int cH = pack_children_height_adj(order, lay, innerW, isEdge); int sep = (cH > 0 && gH > 0) ? 1 : 0; int innerH = cH + sep + gH; if (innerH < 1) innerH = 1; int W = innerW + 2*border; int H = innerH + 2*border; if (W > Kmax || H > Kmax) continue; long long metric = (long long)max(W, H) * 1000000LL + (long long)(W + H); if (metric < bestMetric) { bestMetric = metric; bestInnerW = innerW; bestInnerH = innerH; bestChildrenH = cH; bestSep = sep; bestGadgetPlacement = 0; bestOrder = order; } } } // Try gadget to the right of children if (gW > 0 && gH > 0) { for (int childW = max(1, maxChildW); childW <= Kmax - 2*border; childW++) { int cH = pack_children_height_adj(order, lay, childW, isEdge); int totalW = childW + 1 + gW; // 1-cell gap + gadget width if (cH < 1 && gH < 1) continue; int innerH = max(cH, gH); if (innerH < 1) innerH = 1; int W = totalW + 2*border; int H = innerH + 2*border; if (W > Kmax || H > Kmax) continue; long long metric = (long long)max(W, H) * 1000000LL + (long long)(W + H); if (metric < bestMetric) { bestMetric = metric; bestInnerW = totalW; bestInnerH = innerH; bestChildrenH = cH; bestSep = 0; bestGadgetPlacement = 1; bestOrder = order; } } } } if (bestInnerW == -1) return false; L.order = bestOrder; L.innerW = bestInnerW; L.innerH = bestInnerH; L.w = bestInnerW + 2*border; L.h = bestInnerH + 2*border; L.childrenH = bestChildrenH; L.sep = bestSep; L.gadgetPlacement = bestGadgetPlacement; if (bestGadgetPlacement == 0) { L.gadgetStartY = border + bestChildrenH + bestSep; L.gadgetX = 0; L.gadgetY = bestChildrenH + bestSep; } else { int childW = bestInnerW - 1 - gW; L.gadgetX = childW + 1; L.gadgetY = 0; } int packW = (bestGadgetPlacement == 1) ? (bestInnerW - 1 - gW) : bestInnerW; pack_children_height_adj(L.order, lay, packW, isEdge, &L.pos); lay[u] = std::move(L); return true; } void paint(int u, int top, int left, vector>& C, bool isRoot = false) { const Layout& L = lay[u]; int border = isRoot ? 0 : 1; // For 1x1 leaf nodes, just paint the single cell if (L.w == 1 && L.h == 1) { C[top][left] = u; return; } for (int i = 0; i < L.h; i++) { for (int j = 0; j < L.w; j++) { C[top + i][left + j] = u; } } for (size_t i = 0; i < L.order.size(); i++) { int v = L.order[i]; int y = L.pos[i].first; int x = L.pos[i].second; paint(v, top + border + y, left + border + x, C); } if (!L.gadgetCells.empty()) { int baseY = top + border + L.gadgetY; int baseX = left + border + L.gadgetX; for (auto& gc : L.gadgetCells) { C[baseY + gc.row][baseX + gc.col] = gc.color; } } } int try_build(int r, int mode) { build_spanning_tree_from(r, mode); assign_gadgets(); int Kstart = max(3, N); lay.assign(N + 1, Layout()); for (int K = Kstart; K <= 240; K++) { lay.assign(N + 1, Layout()); if (!dfs_size(r, K, true)) continue; int rw = lay[r].w, rh = lay[r].h; if (rw <= K && rh <= K) { return max(rw, rh); } } return 241; } vector> create_map() { if (N == 1) return vector>(1, vector(1, 1)); adj.assign(N + 1, {}); memset(isEdge, 0, sizeof(isEdge)); for (int i = 0; i < M; i++) { int a = A[i], b = B[i]; isEdge[a][b] = isEdge[b][a] = true; adj[a].push_back(b); adj[b].push_back(a); } int bestK = 241; int bestRoot = 1; int bestMode = 0; for (int mode = 0; mode <= 1; mode++) { for (int r = 1; r <= N; r++) { int K = try_build(r, mode); if (K < bestK) { bestK = K; bestRoot = r; bestMode = mode; } } } build_spanning_tree_from(bestRoot, bestMode); assign_gadgets(); lay.assign(N + 1, Layout()); dfs_size(bestRoot, bestK, true); int Kfinal = bestK; if (Kfinal > 240) Kfinal = 240; vector> C(Kfinal, vector(Kfinal, bestRoot)); paint(bestRoot, 0, 0, C, true); return C; } }; static vector> create_map(int N, int M, vector A, vector B) { Builder b; b.N = N; b.M = M; b.A = std::move(A); b.B = std::move(B); return b.create_map(); } 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 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\n"; for (int i = 0; i < K; i++) { for (int j = 0; j < K; j++) { if (j) cout << ' '; cout << C[i][j]; } cout << "\n"; } } return 0; }