package main
import (
"bufio"
"container/heap"
"fmt"
"os"
"strconv"
)
type Elem struct {
val int
row int
col int
}
type myheap []Elem
func (m myheap) Len() int { return len(m) }
func (m myheap) Less(i, j int) bool { return m[i].val > m[j].val }
func (m myheap) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
func (m *myheap) Pop() interface{} {
x := (*m)[len(*m)-1]
(*m) = (*m)[:len(*m)-1]
return x
}
func (m *myheap) Push(x interface{}) {
*m = append(*m, x.(Elem))
}
func main() {
for {
scanner := bufio.NewScanner(os.Stdin)
// 设置大缓冲区,防止大输入时出错
buf := make([]byte, 1024*1024) // 1MB
scanner.Buffer(buf, 1024*1024)
scanner.Split(bufio.ScanWords) // 按单词扫描(空格、换行等分隔)
// 读取第一行:n
scanner.Scan()
n, _ := strconv.Atoi(scanner.Text())
if n == 0 {
break
}
scanner.Scan()
k, _ := strconv.Atoi(scanner.Text())
arr := make([][]int, n)
for i := 0; i < n; i++ {
scanner.Scan()
m, _ := strconv.Atoi(scanner.Text())
arr[i] = make([]int, m)
for j := 0; j < m; j++ {
scanner.Scan()
arr[i][j], _ = strconv.Atoi(scanner.Text())
}
}
result := process(arr, k)
for i:=0; i<len(result); i++ {
fmt.Printf("%d ", result[i])
}
}
}
func process(arr [][]int, k int) []int {
res := []int{}
var my myheap
heap.Init(&my)
for i, r := range arr {
if len(r) > 0 {
heap.Push(&my, Elem{
val: r[len(r)-1],
row: i,
col: len(r) - 1,
})
}
}
// 大根堆思路 完事
for i := 0; i < k && my.Len() > 0; i++ {
e := heap.Pop(&my).(Elem)
if e.col-1 >= 0 {
heap.Push(&my, Elem{
val: arr[e.row][e.col-1],
row: e.row,
col: e.col - 1,
})
}
res = append(res, e.val)
}
return res
}