fn rand_memory() -> usize { Box::into_raw(Box::new("I hope this is a random number")) as usize } fn rand() -> usize { static mut X: usize = 0; unsafe { if X == 0 { X = rand_memory(); } X ^= X << 13; X ^= X >> 17; X ^= X << 5; X } } fn shuffle(a: &mut [T]) { for i in 1..a.len() { let p = rand() % (i + 1); a.swap(i, p); } } mod util { pub trait Join { fn join(self, sep: &str) -> String; } impl Join for I where I: Iterator, T: std::fmt::Display, { fn join(self, sep: &str) -> String { let mut s = String::new(); use std::fmt::*; for (i, v) in self.enumerate() { if i > 0 { write!(&mut s, "{}", sep).ok(); } write!(&mut s, "{}", v).ok(); } s } } } // ---------- begin scannner ---------- #[allow(dead_code)] mod scanner { use std::str::FromStr; pub struct Scanner<'a> { it: std::str::SplitWhitespace<'a>, } impl<'a> Scanner<'a> { pub fn new(s: &'a String) -> Scanner<'a> { Scanner { it: s.split_whitespace(), } } pub fn next(&mut self) -> T { self.it.next().unwrap().parse::().ok().unwrap() } pub fn next_bytes(&mut self) -> Vec { self.it.next().unwrap().bytes().collect() } pub fn next_chars(&mut self) -> Vec { self.it.next().unwrap().chars().collect() } pub fn next_vec(&mut self, len: usize) -> Vec { (0..len).map(|_| self.next()).collect() } } } // ---------- end scannner ---------- use std::collections::*; use std::io::Write; type Map = BTreeMap; type Set = BTreeSet; type Deque = VecDeque; fn main() { use std::io::Read; let mut s = String::new(); std::io::stdin().read_to_string(&mut s).unwrap(); let mut sc = scanner::Scanner::new(&s); let out = std::io::stdout(); let mut out = std::io::BufWriter::new(out.lock()); run(&mut sc, &mut out); } fn run(sc: &mut scanner::Scanner, out: &mut std::io::BufWriter) { let t: u32 = sc.next(); for _ in 0..t { let n: usize = sc.next(); let mut e = vec![vec![]; n]; for e in e.iter_mut() { let m = sc.next::(); e.resize(m, 0usize); for e in e.iter_mut() { *e = sc.next::() - 1; } } let (q, vc, ec) = solve(e.clone()); let mut list = vec![vec![]; n - 1]; for (e, ec) in e.iter().zip(ec.iter()) { for (e, ec) in e.iter().zip(ec.iter()) { list[*e].push(*ec); } } assert!(list.iter().all(|l| l[0] != l[1])); let mut set = Set::new(); writeln!(out, "{}", q).ok(); for (vc, ec) in vc.iter().zip(ec.iter()) { for &c in ec.iter() { assert!(set.insert((vc, c))); } use util::*; writeln!(out, "{} {}", *vc + 1, ec.iter().map(|a| *a + 1).join(" ")).ok(); } } } fn solve(e: Vec>) -> (usize, Vec, Vec>) { let n = e.len(); let mut list = vec![vec![]; n - 1]; for (i, e) in e.iter().enumerate() { for (j, e) in e.iter().enumerate() { list[*e].push((i, j)); } } let mut tree = vec![vec![]; n]; for (i, v) in list.iter().enumerate() { let (a, b) = (v[0].0, v[1].0); tree[a].push((b, i)); tree[b].push((a, i)); } let deg = e.iter().map(|e| e.len()).collect::>(); let root = (0..n).max_by_key(|v| deg[*v]).unwrap(); let mut topo = vec![root]; let mut parent = vec![n; n]; for i in 0..n { let v = topo[i]; for (u, k) in tree[v].clone() { parent[u] = k; tree[u].retain(|p| p.0 != v); topo.push(u); } } let (list, tree, topo, parent) = (list, tree, topo, parent); let mut q = (1..) .find(|&p| p * p >= 2 * (n - 1)) .unwrap() .max(*deg.iter().max().unwrap()); if greedy(°, q).is_none() { q += 1; } let q = q; let vc = greedy(°, q).unwrap(); let mut memo = vec![vec![]; q]; for (i, vc) in vc.iter().enumerate() { memo[*vc].push(i); } let mut ans = vec![]; while { let mut used = vec![false; q]; let mut color_list = vec![vec![]; n]; for memo in memo.iter() { let sum = memo.iter().map(|v| deg[*v]).sum::(); if 2 * sum <= q { for &v in memo.iter() { let mut c = vec![0; deg[v]]; for c in c.iter_mut() { let mut x = rand() % q; while used[x] { x = rand() % q; } *c = x; used[x] = true; } color_list[v] = c; } for x in memo.iter().flat_map(|v| color_list[*v].iter()) { used[*x] = false; } } else { let mut perm = (0..q).collect::>(); shuffle(&mut perm); for &v in memo.iter() { let mut c = vec![0; deg[v]]; for c in c.iter_mut() { *c = perm.pop().unwrap(); } color_list[v] = c; } } } let mut tmp = e.iter().map(|e| vec![q; e.len()]).collect::>(); let mut dp = vec![q; n]; for &v in topo.iter().rev() { let mut child_color = vec![]; for &(u, k) in tree[v].iter() { child_color.push((dp[u], u, k)); } child_color.sort(); let mut pos = Map::new(); for (i, &(c, _, _)) in child_color.iter().enumerate() { pos.entry(c).or_insert((i, i)).1 = i; } let mut clist = std::mem::take(&mut color_list[v]); let mut buf = vec![!0; clist.len()]; let mut range = (0, clist.len()); for c in clist.iter_mut() { if let Some(&(l, r)) = pos.get(c) { range.0 = range.0.max(r - l + 1); buf[l] = *c; *c = !0; } } if v == root && range.0 == range.1 { break; } assert!(range.0 < range.1); clist.retain(|c| *c != !0); for buf in buf.iter_mut() { if *buf == !0 { *buf = clist.pop().unwrap(); } } let shift = rand() % (range.1 - range.0) + range.0; buf.rotate_right(shift); for (&color, &(dp, u, k)) in buf.iter().zip(child_color.iter()) { assert!(dp != color); let pos = list[k].iter().find(|p| p.0 == v).unwrap().1; tmp[v][pos] = color; } if v != root { let c = buf.pop().unwrap(); dp[v] = c; let k = parent[v]; let pos = list[k].iter().find(|p| p.0 == v).unwrap().1; tmp[v][pos] = c; } if v == root { ans = tmp; break; } } ans.is_empty() } {} (q, vc, ans) } fn greedy(a: &[usize], q: usize) -> Option> { let n = a.len(); assert!(a.iter().sum::() == 2 * (n - 1)); let mut rem = vec![q; q]; let mut h = std::collections::BinaryHeap::new(); for i in 0..q { h.push((q, i)); } let mut ans = vec![0; n]; let mut ord = (0..n).collect::>(); ord.sort_by_key(|v| a[*v]); for x in ord.into_iter().rev() { let a = a[x]; let ans = &mut ans[x]; let (_, k) = h.pop().unwrap(); if rem[k] < a { return None; } *ans = k; rem[k] -= a; h.push((rem[k], k)); } Some(ans) }