use proconio::input; use std::collections::HashSet; //const LOCAL_EXECUTE:bool = true; const TARGET: i64 = 500_000_000_000_000_000; const OPERATION_LIMIT: usize = 50; fn operatioin(u: usize, v: usize, ab: &mut [(i64, i64)]) { let (a1, b1) = ab[u]; let (a2, b2) = ab[v]; let am = (a1 + a2) / 2; let bm = (b1 + b2) / 2; ab[u] = (am, bm); ab[v] = (am, bm); } fn calc_score(mut ab: Vec<(i64, i64)>, op: &[(usize, usize)]) -> i64 { for &(u, v) in op { operatioin(u, v, &mut ab); } let (a, b) = ab[0]; let v1 = a.abs_diff(TARGET); let v2 = b.abs_diff(TARGET); let m = v1.max(v2); let x = op.len() as i64; if m == 0 { return 2000050 - x; } let score = 2000000.0 - 100000.0 * f64::log10(m as f64 + 1.0); score as i64 } fn solver1(n: usize, mut ab: Vec<(i64, i64)>) -> Vec<(usize, usize)> { // いったん全部の平均に近づくようにやってみる // a_max - a_min と b_max - b_min の一番大きな組み合わせのカードで平均を取る let mut ans = vec![]; for _ in 0..OPERATION_LIMIT { // let mut dist_max = -1; // let mut i = 0; // let mut j = 0; // for ii in 0..(n - 1) { // let (ai, bi) = ab[ii]; // for jj in (ii + 1)..n { // let (aj, bj) = ab[jj]; // let dist = (ai - aj).abs().max((bi - bj).abs()); // if dist > dist_max { // dist_max = dist; // i = ii; // j = jj // } // } // } let (i, j) = (0..n - 1) .into_iter() .map(|i| { let (dist, j) = (i + 1..n) .into_iter() .map(|j| { let (ai, bi) = ab[i]; let (aj, bj) = ab[j]; let dist = ai.abs_diff(aj).max(bi.abs_diff(bj)); (dist, j) }) .max() .unwrap(); (dist, (i, j)) }) .max() .unwrap() .1; ans.push((i, j)); operatioin(i, j, &mut ab); } ans } fn find_best(ab: &[(i64, i64)], ignore_idx: &mut HashSet) { let (mut a_sum, mut b_sum) = (0, 0); for &(a, b) in ab { a_sum -= TARGET; a_sum += a; b_sum -= TARGET; b_sum += b; } let mut best_dist = i64::MAX; let mut best_idx = 1; for (i, &(a, b)) in ab.iter().enumerate() { if i == 0 { continue; } if ignore_idx.contains(&i) { continue; } let dist = (a_sum - a + TARGET).abs() + (b_sum - b + TARGET).abs(); if dist < best_dist { best_dist = dist; best_idx = i; } } ignore_idx.insert(best_idx); } fn solver2(n: usize, mut ab: Vec<(i64, i64)>) -> Vec<(usize, usize)> { // 仲間外れを1枚決めて、それ以外で solver1 の要領で中心に寄る // 2段階に分けて2回それを行う let mut ans = vec![]; let mut ignore_idx = HashSet::new(); for loops in 0..50 { if loops % 10 == 0 { find_best(&ab, &mut ignore_idx); } let mut dist_max = -1; let mut i = 0; let mut j = 0; for ii in 0..(n - 1) { if ignore_idx.contains(&ii) { continue; } let (ai, bi) = ab[ii]; for jj in (ii + 1)..n { if ignore_idx.contains(&jj) { continue; } let (aj, bj) = ab[jj]; let dist = (ai - aj).abs().max((bi - bj).abs()); if dist > dist_max { dist_max = dist; i = ii; j = jj } } } ans.push((i, j)); operatioin(i, j, &mut ab); } ans } fn solver3(n: usize, mut ab: Vec<(i64, i64)>) -> Vec<(usize, usize)> { // 仲間外れを1枚決めて、それ以外で solver1 の要領で中心に寄る // 2段階に分けて2回それを行う // solver2ベースで48回おこない、最後の2回は全探索 let mut ans = vec![]; let mut ignore_idx = HashSet::new(); for loops in 0..48 { if loops % 5 == 0 { find_best(&ab, &mut ignore_idx); } let mut dist_max = -1; let mut i = 0; let mut j = 0; for ii in 0..(n - 1) { if ignore_idx.contains(&ii) { continue; } let (ai, bi) = ab[ii]; for jj in (ii + 1)..n { if ignore_idx.contains(&jj) { continue; } let (aj, bj) = ab[jj]; let dist = (ai - aj).abs().max((bi - bj).abs()); if dist > dist_max { dist_max = dist; i = ii; j = jj } } } ans.push((i, j)); operatioin(i, j, &mut ab); } // ここから最後の2回を全探索. 脳筋実装. // こういうのをAIでいい感じにするべき let (mut best_i0, mut best_i1, mut best_i2, mut best_i3) = (1, 2, 3, 4); let mut best_dist = { let &(a, b) = &ab[0]; ((a - TARGET).abs()).max((b - TARGET).abs()) }; for i0 in 0..(n - 1) { for i1 in (i0 + 1)..n { let a0 = (&ab[i0].0 + &ab[i1].0) / 2; let b0 = (&ab[i0].1 + &ab[i1].1) / 2; let i2 = 0; for i3 in 1..n { if i0 == i2 { if i1 == i3 { let dist = ((a0 - TARGET).abs()).max((b0 - TARGET).abs()); if dist < best_dist { best_dist = dist; best_i0 = i0; best_i1 = i1; best_i2 = i2; best_i3 = i3; } } else { let dist = ((a0 + &ab[i3].0) / 2 - TARGET) .abs() .max(((b0 + &ab[i3].1) / 2 - TARGET).abs()); if dist < best_dist { best_dist = dist; best_i0 = i0; best_i1 = i1; best_i2 = i2; best_i3 = i3; } } } else { let mut dist = 0; if i0 == i3 || i1 == i3 { dist = ((a0 + &ab[i2].0) / 2 - TARGET) .abs() .max(((b0 + &ab[i2].1) / 2 - TARGET).abs()); } else { dist = ((&ab[i2].0 + &ab[i3].0) / 2 - TARGET) .abs() .max(((&ab[i2].1 + &ab[i3].1) / 2 - TARGET).abs()); } if dist < best_dist { best_dist = dist; best_i0 = i0; best_i1 = i1; best_i2 = i2; best_i3 = i3; } } } } } ans.push((best_i0, best_i1)); ans.push((best_i2, best_i3)); ans } fn solver4(n: usize, mut ab: Vec<(i64, i64)>) -> Vec<(usize, usize)> { // 仲間外れを決めるのはおなじ // ペアは必ず0とのペアとする let mut ans = vec![]; let mut ignore_idx = HashSet::new(); for loops in 0..48 { if loops % 3 == 0 { find_best(&ab, &mut ignore_idx); } let mut dist_max = -1; let mut i = 0; let mut j = 0; let (ai, bi) = ab[0]; for jj in 1..n { if ignore_idx.contains(&jj) { continue; } let (aj, bj) = ab[jj]; let dist = (ai - aj).abs().max((bi - bj).abs()); if dist > dist_max { dist_max = dist; j = jj } } ans.push((i, j)); operatioin(i, j, &mut ab); } // ここから最後の2回を全探索. 脳筋実装. // こういうのをAIでいい感じにするべき let (mut best_i0, mut best_i1, mut best_i2, mut best_i3) = (1, 2, 3, 4); let mut best_dist = { let &(a, b) = &ab[0]; ((a - TARGET).abs()).max((b - TARGET).abs()) }; for i0 in 0..(n - 1) { for i1 in (i0 + 1)..n { let a0 = (&ab[i0].0 + &ab[i1].0) / 2; let b0 = (&ab[i0].1 + &ab[i1].1) / 2; let i2 = 0; for i3 in 1..n { if i0 == i2 { if i1 == i3 { let dist = ((a0 - TARGET).abs()).max((b0 - TARGET).abs()); if dist < best_dist { best_dist = dist; best_i0 = i0; best_i1 = i1; best_i2 = i2; best_i3 = i3; } } else { let dist = ((a0 + &ab[i3].0) / 2 - TARGET) .abs() .max(((b0 + &ab[i3].1) / 2 - TARGET).abs()); if dist < best_dist { best_dist = dist; best_i0 = i0; best_i1 = i1; best_i2 = i2; best_i3 = i3; } } } else { let mut dist = 0; if i0 == i3 || i1 == i3 { dist = ((a0 + &ab[i2].0) / 2 - TARGET) .abs() .max(((b0 + &ab[i2].1) / 2 - TARGET).abs()); } else { dist = ((&ab[i2].0 + &ab[i3].0) / 2 - TARGET) .abs() .max(((&ab[i2].1 + &ab[i3].1) / 2 - TARGET).abs()); } if dist < best_dist { best_dist = dist; best_i0 = i0; best_i1 = i1; best_i2 = i2; best_i3 = i3; } } } } } ans.push((best_i0, best_i1)); ans.push((best_i2, best_i3)); ans } fn main() { input! { n:usize, ab:[(i64, i64); n] } // solve let ans = solver4(n, ab.clone()); eprintln!("{}", calc_score(ab.clone(), &ans)); println!("{}", ans.len()); for &(u, v) in &ans { println!("{} {}", u + 1, v + 1); } }