https://atcoder.jp/contests/abc353/tasks/abc353_f
K = 1なら単なるマンハッタン距離です。
K > 1なら、例1のように両側から大きいタイルに出て、大きなタイル間の距離を調べます。そのとき斜めの成分とx軸、y軸のどちらかに分解します。斜め成分は一つ進むごとに2回タイルを通過します。それ以外の成分はK > 2なら2つ進むごとに角のタイルを使って4回タイルを通過しますが、K = 2の場合だけはそのまま突っ切って3回タイルを通過します。
// Tile Distance #![allow(non_snake_case)] use std::cmp::min; //////////////////// library //////////////////// fn read<T: std::str::FromStr>() -> T { let mut line = String::new(); std::io::stdin().read_line(&mut line).ok(); line.trim().parse().ok().unwrap() } fn read_vec<T: std::str::FromStr>() -> Vec<T> { read::<String>().split_whitespace() .map(|e| e.parse().ok().unwrap()).collect() } fn div(n: i64, d: i64) -> i64 { if n < 0 { (n - d + 1) / d } else { n / d } } fn modulo(n: i64, d: i64) -> i64 { if n < 0 { let r = n % d; if r == 0 { r } else { r + d } } else { n % d } } //////////////////// Point //////////////////// #[derive(Clone, Copy)] struct Point { x: i64, y: i64 } use std::ops::Sub; impl Sub for Point { type Output = Self; fn sub(self, other: Self) -> Self { let x = self.x - other.x; let y = self.y - other.y; Point { x, y } } } impl Point { fn new(x: i64, y: i64) -> Point { Point { x, y } } fn read() -> Point { let v = read_vec(); Point::new(v[0], v[1]) } } //////////////////// process //////////////////// fn read_input() -> (i64, Point, Point) { let K: i64 = read(); let S = Point::read(); let T = Point::read(); (K, S, T) } fn F1(S: Point, T: Point) -> i64 { let d = S - T; d.x.abs() + d.y.abs() } fn is_large(pt: Point, K: i64) -> bool { (div(pt.x, K) + div(pt.y, K)) % 2 == 1 } fn large_to_large(pt1: Point, pt2: Point, K: i64) -> i64 { let x1 = div(pt1.x, K); let y1 = div(pt1.y, K); let x2 = div(pt2.x, K); let y2 = div(pt2.y, K); let dx = (x1 - x2).abs(); let dy = (y1 - y2).abs(); let c = if K == 2 { 3 } else { 4 }; (dx - dy).abs() * c / 2 + min(dx, dy) * 2 } fn to_large(pt: Point, dir: i32, K: i64) -> (i64, Point) { if dir == 0 { let r = modulo(pt.x, K); (r + 1, Point::new(pt.x - K, pt.y)) } else if dir == 1 { let r = modulo(pt.y, K); (K - r, Point::new(pt.x, pt.y + K)) } else if dir == 2 { let r = modulo(pt.x, K); (K - r, Point::new(pt.x + K, pt.y)) } else { let r = modulo(pt.y, K); (r + 1, Point::new(pt.x, pt.y - K)) } } fn large_to_small(pt1: Point, pt2: Point, K: i64) -> i64 { let mut ds: Vec<i64> = vec![]; for dir in 0..4 { let (d, pt3) = to_large(pt2, dir, K); ds.push(d + large_to_large(pt1, pt3, K)) } ds.into_iter().min().unwrap() } fn small_to_small(pt1: Point, pt2: Point, K: i64) -> i64 { let mut ds: Vec<i64> = vec![F1(pt1, pt2)]; for dir1 in 0..4 { for dir2 in 0..4 { let (d1, pt3) = to_large(pt1, dir1, K); let (d2, pt4) = to_large(pt2, dir2, K); ds.push(d1 + large_to_large(pt3, pt4, K) + d2) } } ds.into_iter().min().unwrap() } fn F2(K: i64, S: Point, T:Point) -> i64 { if is_large(S, K) { if is_large(T, K) { large_to_large(S, T, K) } else { large_to_small(S, T, K) } } else { if is_large(T, K) { large_to_small(T, S, K) } else { small_to_small(S, T, K) } } } fn F(K: i64, S: Point, T: Point) -> i64 { if K == 1 { F1(S, T) } else { F2(K, S, T) } } fn main() { let (K, S, T) = read_input(); println!("{}", F(K, S, T)) }