#include #include #include #include #include using namespace std; bool is_sq(int x) { int r = round(sqrt(x)); return r * r == x; } int N; vector x_arr, y_arr; int C_weight; // 利用可能な重みのリスト vector available; // グラフの構築 (バックトラッキング) bool build_graph(int idx, vector& avail, mt19937& rng) { if (idx > N) return true; // 利用可能な重みから2つを選ぶ候補を作成 vector> cands; for (size_t i = 0; i < avail.size(); ++i) { for (size_t j = 0; j < avail.size(); ++j) { if (i == j) continue; cands.push_back({avail[i], avail[j]}); } } shuffle(cands.begin(), cands.end(), rng); for (auto& p : cands) { int x = p.first; int y = p.second; // 頂点 idx に x と y を仮置き x_arr[idx] = x; y_arr[idx] = y; bool ok_node = true; // 1. 頂点1へのパスが平方数になるか bool ok1 = is_sq(x) || is_sq(y + C_weight); for (int w = 3; w < idx && !ok1; ++w) if (is_sq(y + y_arr[w] + x_arr[w])) ok1 = true; if (!ok1) continue; // 2. 頂点2へのパスが平方数になるか bool ok2 = is_sq(y) || is_sq(x + C_weight); for (int w = 3; w < idx && !ok2; ++w) if (is_sq(x + x_arr[w] + y_arr[w])) ok2 = true; if (!ok2) continue; // 3. 既存の頂点 j (3 <= j < idx) へのパスが平方数になるか for (int j = 3; j < idx; ++j) { bool ok_j = is_sq(x + x_arr[j]) || is_sq(y + y_arr[j]) || is_sq(x + C_weight + y_arr[j]) || is_sq(y + C_weight + x_arr[j]); for (int w = 3; w < idx && !ok_j; ++w) { if (w == j) continue; if (is_sq(x + x_arr[w] + y_arr[w] + y_arr[j])) ok_j = true; if (is_sq(y + y_arr[w] + x_arr[w] + x_arr[j])) ok_j = true; } if (!ok_j) { ok_node = false; break; } } if (ok_node) { vector next_avail; for (int v : avail) { if (v != x && v != y) next_avail.push_back(v); } if (build_graph(idx + 1, next_avail, rng)) return true; } } return false; } struct Edge { int to, weight, id; }; vector> adj; vector best_path; vector current_path; vector vis; // 平方数になるパスを見つけるためのDFS bool dfs_path(int u, int target, int sum) { if (u == target) { if (is_sq(sum)) { best_path = current_path; return true; } return false; } for (auto& edge : adj[u]) { if (!vis[edge.to]) { vis[edge.to] = true; current_path.push_back(edge.id); if (dfs_path(edge.to, target, sum + edge.weight)) return true; current_path.pop_back(); vis[edge.to] = false; } } return false; } int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); if (!(cin >> N)) return 0; mt19937 rng(1337); // 固定シードを用いて再現性を確保 x_arr.resize(N + 1); y_arr.resize(N + 1); while (true) { vector nums; for (int i = 1; i <= 200; ++i) nums.push_back(i); shuffle(nums.begin(), nums.end(), rng); // C_weight は平方数であると見つかりやすい int c_idx = 0; for (int i = 0; i < nums.size(); ++i) { if (is_sq(nums[i])) { c_idx = i; break; } } C_weight = nums[c_idx]; nums.erase(nums.begin() + c_idx); if (N <= 2 || build_graph(3, nums, rng)) { break; } } // グラフの構築と出力 int M = (N == 2) ? 1 : 2 * N - 3; cout << M << "\n"; adj.assign(N + 1, vector()); int edge_id = 1; auto add_edge = [&](int u, int v, int w) { cout << u << " " << v << " " << w << "\n"; adj[u].push_back({v, w, edge_id}); adj[v].push_back({u, w, edge_id}); edge_id++; }; add_edge(1, 2, C_weight); for (int i = 3; i <= N; ++i) { add_edge(1, i, x_arr[i]); add_edge(2, i, y_arr[i]); } // 各ペアについて平方数パスを探す vis.assign(N + 1, false); for (int i = 1; i <= N; ++i) { for (int j = i + 1; j <= N; ++j) { current_path.clear(); best_path.clear(); vis.assign(N + 1, false); vis[i] = true; dfs_path(i, j, 0); cout << best_path.size() << "\n"; for (size_t k = 0; k < best_path.size(); ++k) { cout << best_path[k] << (k + 1 == best_path.size() ? "" : " "); } cout << "\n"; } } return 0; }