import sys import random def solve(): # 入力を受け取る (N) input_data = sys.stdin.read().split() if not input_data: return N = int(input_data[0]) if N == 2: # N=2 の自明なケース print(1) # 辺数 M print("1 2 1") # u v w print("1 1") # length_of_path, edge_id return # 重みの候補 (1〜200) U = list(range(1, 201)) # 辺リスト: (u, v, w, id) edges = [] # DP用のマスク配列: masks[a][b] に a と b の間の単純パスの長さ(の集合)をビットで持つ # a > b となるようにアクセスする masks = [[0] * (N + 1) for _ in range(N + 1)] # 平方数のビットマスク (最悪パス長は 200 * 100 = 20000 なので 150^2=22500 まであれば十分) SQUARES_MASK = 0 for i in range(1, 151): SQUARES_MASK |= (1 << (i * i)) # 初期状態 (頂点1と2) U.remove(1) edges.append((1, 2, 1, 1)) masks[2][1] = (1 << 1) masks[1][1] = 1 masks[2][2] = 1 edge_id_counter = 2 vertex_info = [None] * (N + 1) # 乱数シード固定で決定的にする(ジャッジ側での実行の一貫性を担保) rng = random.Random(42) for i in range(3, N + 1): # 利用可能な重みのペアを総和が小さい順に試す (パス長が爆発して平方数の上限を超えないようにする) U_pairs = [] for xi in range(len(U)): for yi in range(xi + 1, len(U)): U_pairs.append((U[xi], U[yi], xi, yi)) U_pairs.sort(key=lambda item: item[0] + item[1]) # 接続先候補 (u, v) をランダムに並べる(特定の部分木に偏るのを防ぐ) candidates_uv = [] for u in range(1, i): for v in range(1, i): if u != v: candidates_uv.append((u, v)) rng.shuffle(candidates_uv) found = False best_u = best_v = best_x = best_y = idx_x = idx_y = -1 for x, y, xi, yi in U_pairs: if found: break for u, v in candidates_uv: ok = True # i番目の頂点を追加したとき、すべての k < i について平方数パスが1つ以上作れるか判定 for k in range(1, i): mask_u = masks[max(u, k)][min(u, k)] mask_v = masks[max(v, k)][min(v, k)] new_mask = (mask_u << x) | (mask_v << y) if (new_mask & SQUARES_MASK) == 0: ok = False break if ok: found = True best_u, best_v, best_x, best_y = u, v, x, y idx_x, idx_y = xi, yi break if not found: raise Exception(f"Failed to find valid connection for vertex {i}") ex = edge_id_counter ey = edge_id_counter + 1 edge_id_counter += 2 vertex_info[i] = (best_u, best_v, best_x, best_y, ex, ey) edges.append((i, best_u, best_x, ex)) edges.append((i, best_v, best_y, ey)) # 使った重みをリストから削除 (インデックスがズレないよう大きい方からpop) U.pop(max(idx_x, idx_y)) U.pop(min(idx_x, idx_y)) # 決定した状態をもとにマスクを確定 masks[i][i] = 1 for k in range(1, i): mask_u = masks[max(best_u, k)][min(best_u, k)] mask_v = masks[max(best_v, k)][min(best_v, k)] masks[i][k] = (mask_u << best_x) | (mask_v << best_y) def get_path(a, b): """ a から b (a > b) への条件を満たす単純パスを復元する """ valid_sq = masks[a][b] & SQUARES_MASK if valid_sq == 0: raise Exception("No path found!") # 存在する平方数パスのうち最小の長さを採用 L = (valid_sq & -valid_sq).bit_length() - 1 path = [] curr_max = a curr_min = b while curr_max != curr_min: u, v, x, y, ex, ey = vertex_info[curr_max] # u 側から来たルートか検証 next_max_u, next_min_u = max(u, curr_min), min(u, curr_min) if L >= x and (masks[next_max_u][next_min_u] & (1 << (L - x))): path.append(ex) L -= x curr_max, curr_min = next_max_u, next_min_u continue # v 側から来たルートか検証 next_max_v, next_min_v = max(v, curr_min), min(v, curr_min) if L >= y and (masks[next_max_v][next_min_v] & (1 << (L - y))): path.append(ey) L -= y curr_max, curr_min = next_max_v, next_min_v continue raise Exception("Traceback failed!") return path # === 出力パート === M = len(edges) # グラフの出力 (フォーマットの仕様に合わせてここは適宜調整してください) print(f"{M}") for u, v, w, eid in edges: print(f"{u} {v} {w}") # パスの出力 (1 <= i < j <= N) for i in range(1, N): for j in range(i + 1, N + 1): p = get_path(j, i) p.reverse() # i から j への向かう順番にするため反転 # len(Q) e_1 e_2 ... の形式で出力 print(f"{len(p)} " + " ".join(map(str, p))) if __name__ == '__main__': solve()