use ac_library::Dsu; use proconio::{input, marker::Bytes}; const DRY_THRESHOLD: u8 = 5; fn neighbors(v: usize, n: usize) -> impl Iterator { let (r, c) = (v / n, v % n); [ (r > 0).then(|| v - n), (r + 1 < n).then(|| v + n), (c > 0).then(|| v - 1), (c + 1 < n).then(|| v + 1), ] .into_iter() .flatten() } fn largest_group(active: &[bool], n: usize) -> Vec { let mut dsu = Dsu::new(n * n); for v in 0..n * n { if active[v] { for u in neighbors(v, n) { if active[u] { dsu.merge(v, u); } } } } let mut best = Vec::new(); for group in dsu.groups() { if active[group[0]] && group.len() > best.len() { best = group; } } best } // Test actual connected components after removing this row or column. fn is_balanced(group: &[usize], n: usize, axis: usize, line: usize) -> bool { let coordinate = |v: usize| if axis == 0 { v / n } else { v % n }; let mut unseen = vec![false; n * n]; for &v in group { unseen[v] = coordinate(v) != line; } let mut stack = Vec::with_capacity(group.len()); for &start in group { if !unseen[start] { continue; } unseen[start] = false; stack.push(start); let mut size = 0; while let Some(v) = stack.pop() { size += 1; if size * 2 > group.len() { return false; } for u in neighbors(v, n) { if unseen[u] { unseen[u] = false; stack.push(u); } } } } true } fn choose_cut(group: &[usize], n: usize) -> Option> { let mut best: Option> = None; // First encountered wins ties: rows, then columns, in ascending order. for axis in 0..2 { for line in 0..n { let cut: Vec<_> = group .iter() .copied() .filter(|&v| (if axis == 0 { v / n } else { v % n }) == line) .collect(); if cut.is_empty() || best.as_ref().is_some_and(|b| cut.len() >= b.len()) { continue; } if is_balanced(group, n, axis, line) { best = Some(cut); } } } best } fn main() { input! { n: usize, k: usize, rows: [Bytes; n] } let mut active = Vec::with_capacity(n * n); for row in rows { assert_eq!(row.len(), n); active.extend(row.into_iter().map(|d| d - b'0' >= DRY_THRESHOLD)); } let mut answer = Vec::new(); while answer.len() < k { let group = largest_group(&active, n); if group.is_empty() { break; } // No alternative cutting rule has been specified if no line qualifies. let mut cut = choose_cut(&group, n) .expect("No straight cut satisfies the specified half-size condition"); cut.sort_unstable(); for v in cut.into_iter().take(k - answer.len()) { active[v] = false; answer.push(v); } } eprintln!("remaining_cut_budget: {}", k - answer.len()); println!("{}", answer.len()); for v in answer { println!("{} {}", v / n, v % n); } }