import sys
def solve():
# 建立一個產生器,避免一次把 50MB 讀進記憶體
def get_input():
for line in sys.stdin:
for word in line.split():
yield int(word)
it = get_input()
try:
t_str = next(it)
except StopIteration:
return
t = int(t_str)
for _ in range(t):
n = next(it)
# 1. 使用 bytearray 儲存 i_prefs (2 bytes 存一個編號)
# 總空間: N * N * 2 bytes (1024*1024*2 = 2MB)
i_prefs = bytearray(n * n * 2)
for i in range(n * n):
val = next(it)
i_prefs[i*2] = val & 0xFF
i_prefs[i*2+1] = (val >> 8) & 0xFF
# 2. 使用 bytearray 儲存 o_ranks (2 bytes 存一個排名)
# 總空間: (N+1) * (N+1) * 2 bytes
o_ranks = bytearray((n + 1) * (n + 1) * 2)
for o_id in range(1, n + 1):
offset = o_id * (n + 1) * 2
for rank in range(n):
i_id = next(it)
pos = offset + i_id * 2
o_ranks[pos] = rank & 0xFF
o_ranks[pos+1] = (rank >> 8) & 0xFF
i_match = [0] * (n + 1)
o_match = [0] * (n + 1)
next_prop = [0] * (n + 1)
# 單身名單
free_i = list(range(n, 0, -1))
while free_i:
curr_i = free_i.pop()
# 從 bytearray 還原 target_o (16-bit)
idx = next_prop[curr_i]
next_prop[curr_i] += 1
p_pos = ((curr_i - 1) * n + idx) * 2
target_o = i_prefs[p_pos] + (i_prefs[p_pos+1] << 8)
if o_match[target_o] == 0:
o_match[target_o] = curr_i
i_match[curr_i] = target_o
else:
old_i = o_match[target_o]
o_offset = target_o * (n + 1) * 2
# 從 bytearray 還原排名進行比較
curr_rank_pos = o_offset + curr_i * 2
old_rank_pos = o_offset + old_i * 2
curr_rank = o_ranks[curr_rank_pos] + (o_ranks[curr_rank_pos+1] << 8)
old_rank = o_ranks[old_rank_pos] + (o_ranks[old_rank_pos+1] << 8)
if curr_rank < old_rank:
o_match[target_o] = curr_i
i_match[curr_i] = target_o
free_i.append(old_i)
else:
free_i.append(curr_i)
# 輸出結果
for i in range(1, n + 1):
sys.stdout.write(f"{i} {i_match[i]}\n")
if __name__ == "__main__":
solve()