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.
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

Please read the guidelines before commenting.


There are no comments at the moment.