class CSR: def __init__(self, n: int, edges: list): self.start = [0] * (n + 1) self.elist = [0] * len(edges) for e in edges: self.start[e[0] + 1] += 1 for i in range(1, n + 1): self.start[i] += self.start[i - 1] counter = self.start[::] # copy for e in edges: self.elist[counter[e[0]]] = e[1] counter[e[0]] += 1 class SccGraph: def __init__(self, n: int = 0): self.__n = n self.__edges = [] def __len__(self): return self.__n def add_edge(self, s: int, t: int): assert 0 <= s < self.__n and 0 <= t < self.__n self.__edges.append([s, t]) def scc_ids(self): g = CSR(self.__n, self.__edges) now_ord = group_num = 0 visited = [] low = [0] * self.__n order = [-1] * self.__n ids = [0] * self.__n parent = [-1] * self.__n for root in range(self.__n): if order[root] == -1: stack = [root, root] while stack: v = stack.pop() if order[v] == -1: visited.append(v) low[v] = order[v] = now_ord now_ord += 1 for i in range(g.start[v], g.start[v + 1]): t = g.elist[i] if order[t] == -1: stack += [t, t] parent[t] = v else: low[v] = min(low[v], order[t]) else: if low[v] == order[v]: while True: u = visited.pop() order[u] = self.__n ids[u] = group_num if u == v: break group_num += 1 if parent[v] != -1: low[parent[v]] = min(low[parent[v]], low[v]) for i, x in enumerate(ids): ids[i] = group_num - 1 - x return group_num, ids def scc(self): """ 強連結成分のリストを返す。この時、リストはトポロジカルソートされている [[強連結成分のリスト], [強連結成分のリスト], ...] """ group_num, ids = self.scc_ids() counts = [0] * group_num for x in ids: counts[x] += 1 groups = [[] for _ in range(group_num)] for i, x in enumerate(ids): groups[x].append(i) return groups class TwoSAT(): def __init__(self, n): self.n = n self.res = [0]*self.n self.scc = SccGraph(2*n) def add_clause(self, i, f, j, g): # assert 0 <= i < self.n # assert 0 <= j < self.n self.scc.add_edge(2*i + (not f), 2*j + g) self.scc.add_edge(2*j + (not g), 2*i + f) def add_or(self, i, f, j, g): self.add_clause(i, f, j, g) def add_xor(self, i, f, j, g): self.add_clause(i, f, j, g) self.add_clause(i, f^1, j, g^1) def add_and(self, i, f, j, g): self.add_clause(i, f, j, g^1) self.add_clause(i, f^1, j, g) self.add_clause(i, f, j, g) def satisfiable(self): """ 条件を足す割当が存在するかどうかを判定する。割当が存在するならばtrue、そうでないならfalseを返す。 """ group_num, ids = self.scc.scc_ids() for i in range(self.n): if ids[2*i] == ids[2*i + 1]: return False self.res[i] = (ids[2*i] < ids[2*i+1]) return True def result(self): """ 最後に呼んだ satisfiable の、クローズを満たす割当を返す。 """ return self.res ############################################################################# import sys input = sys.stdin.readline N=int(input()) if N>2756: print("Impossible") exit() """ xi と xj の少なくとも一つは存在 ( (xi,xj) = (0,1) or (1,0) or (1,1)) add_clause(i,1,j,1) xi と xj は共存できない( (xi,xj) = (0,1) or (1,0) or (0,0)) add_clause(i,0,j,0) xi と xj はペアでのみ存在する( (xi,xj) = (1,1) or (0,0)) add_xor(i,0,j,1) xi と xj の片一方のみが必ず存在する( (xi,xj) = (0,1) or (1,0)) add_xor(i,1,j,1) """ data=[] for _ in range(N): data.append(input().rstrip()) ts = TwoSAT(2*N) for i in range(N): u1=data[i] s11,t11=u1[:1],u1[1:] s12,t12=u1[:2],u1[2:] ts.add_xor(2*i,1,2*i+1,1) for j in range(i+1,N): u2=data[j] s21,t21=u2[:1],u2[1:] s22,t22=u2[:2],u2[2:] if s11==s21 or s11==t21 or t11==s21 or t11==t21: ts.add_or(2*i,0,2*j,0) if s12==s21 or s12==t21 or t12==s21 or t12==t21: ts.add_or(2*i+1,0,2*j,0) if s11==s22 or s11==t22 or t11==s22 or t11==t22: ts.add_or(2*i,0,2*j+1,0) if s12==s22 or s12==t22 or t12==s22 or t12==t22: ts.add_or(2*i+1,0,2*j+1,0) flg=ts.satisfiable() if not flg: print("Impossible") exit() res=ts.result() for i in range(N): u=data[i] if res[2*i]==1: print(u[:1],u[1:]) else: print(u[:2],u[2:])