Editorial for Hội Họa
Remember to use this editorial only when stuck, and not to copy-paste code from it. Please be respectful to the problem author and editorialist.
Submitting an official solution before solving the problem yourself is a bannable offence.
Submitting an official solution before solving the problem yourself is a bannable offence.
use std::{convert::TryInto, fmt::Debug, io::*, iter::*, str::*}; fn main() { let mut stdin = stdin().lock(); let mut writer = BufWriter::new(stdout()); let mut read_line = || -> String { let mut line = String::new(); stdin.read_line(&mut line).unwrap(); line }; let ntest = parse::<usize>(&read_line()); for _ in 0..ntest { // eprintln!("==== test"); let [n, m] = split_arr::<2, usize>(&read_line()); let a = (0..n).map(|_| split_vec::<usize>(&read_line())).collect::<Vec<_>>(); let mut r_next = (0..=n).map(|r| (r + 1) % (n + 1)).collect::<Vec<_>>(); let mut r_prev = (0..=n).map(|r| (r + n) % (n + 1)).collect::<Vec<_>>(); let mut c_next = (0..=m).map(|c| (c + 1) % (m + 1)).collect::<Vec<_>>(); let mut c_prev = (0..=m).map(|c| (c + m) % (m + 1)).collect::<Vec<_>>(); let mut r_cnt_sep = (0..n) .map(|r| (1..m).map(|c| (a[r][c] != a[r][c - 1]) as usize).sum::<usize>()) .collect::<Vec<_>>(); let mut c_cnt_sep = (0..m) .map(|c| (1..n).map(|r| (a[r][c] != a[r - 1][c]) as usize).sum::<usize>()) .collect::<Vec<_>>(); #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] enum Side { Row, Col, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] struct Op { side: Side, idx: usize, val: usize, } fn iter_link(next: &[usize], start: usize, end: usize) -> impl Iterator<Item = usize> + '_ { successors(Some(start), move |&i| Some(next[i])).take_while(move |&i| i != end) } let mut ans = Vec::<Op>::new(); while r_next[n] != n && c_next[m] != m { if let Some((_, r)) = r_cnt_sep.iter().zip(0..).find(|&(&x, _)| x == 0) { let i = c_next[m]; let prv = r_prev[r]; let nxt = r_next[r]; // eprintln!("Row {} has no separation, {}", r, i); ans.push(Op { side: Side::Row, idx: r, val: a[r][i] }); r_cnt_sep[r] = m; // just to exclude from search for i in iter_link(&c_next, i, m) { if prv != n && a[prv][i] != a[r][i] { c_cnt_sep[i] -= 1; } if nxt != n && a[nxt][i] != a[r][i] { c_cnt_sep[i] -= 1; } if nxt != n && prv != n && a[prv][i] != a[nxt][i] { c_cnt_sep[i] += 1; } } r_next[prv] = nxt; r_prev[nxt] = prv; continue; } if let Some((_, c)) = c_cnt_sep.iter().zip(0..).find(|&(&x, _)| x == 0) { let i = r_next[n]; let prv = c_prev[c]; let nxt = c_next[c]; // eprintln!("Column {} has no separation, {}", c, i); ans.push(Op { side: Side::Col, idx: c, val: a[i][c] }); c_cnt_sep[c] = n; // just to exclude from search for i in iter_link(&r_next, i, n) { if prv != m && a[i][prv] != a[i][c] { r_cnt_sep[i] -= 1; } if nxt != m && a[i][nxt] != a[i][c] { r_cnt_sep[i] -= 1; } if nxt != m && prv != m && a[i][prv] != a[i][nxt] { r_cnt_sep[i] += 1; } } c_next[prv] = nxt; c_prev[nxt] = prv; continue; } break; } if r_next[n] != n && c_next[m] != m { writeln!(writer, "NO").unwrap(); continue; } writeln!(writer, "YES").unwrap(); writeln!(writer, "{}", ans.len()).unwrap(); for &Op { side, idx, val } in ans.iter().rev() { match side { Side::Row => writeln!(writer, "0 {} {}", idx + 1, val).unwrap(), Side::Col => writeln!(writer, "1 {} {}", idx + 1, val).unwrap(), } } } } fn parse<T: FromStr>(s: &str) -> T { s.trim().parse::<T>().ok().unwrap() } fn split_vec<T: FromStr>(s: &str) -> Vec<T> { s.trim().split_whitespace().map(parse).collect::<Vec<_>>() } fn split_arr<const N: usize, T: FromStr>(s: &str) -> [T; N] { split_vec::<T>(s).try_into().ok().unwrap() }
Comments