원이 교차하지 않으므로 접하는 경우만 생각하면 됩니다.
원의 지름 끝이 원들에 의해 연결되어 있다면 영역이 2개로 나뉘고, 그 외의 경우에는 1개가 생깁니다.
연결성 확인은 서로소 집합 자료구조를 통해 쉽게 할 수 있지만, x좌표의 범위가 넓어 좌표 압축이 필요합니다.
압축에 O(NlogN), 죄표를 검색하는 데 O(logN), 연결성 확인에 O(log*N)이므로 O(NlogNlog*N)에 해결할 수 있습니다.
코드
use std::io::{stdin, Read};
fn main() {
let mut input = String::new();
stdin().read_to_string(&mut input).unwrap();
let mut input = input.split_ascii_whitespace();
let n = input.next().unwrap().parse().unwrap();
let mut circles = Vec::with_capacity(n);
let mut points = Vec::with_capacity(n * 2);
for _ in 0..n {
let xi: i32 = input.next().unwrap().parse().unwrap();
let ri: u32 = input.next().unwrap().parse().unwrap();
circles.push((xi + ri as i32, ri));
points.push(xi + ri as i32);
points.push(xi - ri as i32);
}
circles.sort_unstable();
points.sort_unstable();
points.dedup();
let mut cuts = Sets::new(points.len());
let mut count = 1;
for (ei, ri) in circles {
let s = points.binary_search(&(ei - (ri * 2) as i32)).unwrap();
let e = points.binary_search(&ei).unwrap();
let rs = cuts.root(s);
let re = cuts.root(e);
if rs == re {
count += 2;
} else {
count += 1;
cuts.join(rs, re);
}
}
println!("{}", count);
}
struct Sets {
up: Vec<usize>,
rank: Vec<usize>,
}
impl Sets {
fn new(n: usize) -> Self {
Self {
up: (0..n).collect(),
rank: vec![1; n],
}
}
fn root(&mut self, mut x: usize) -> usize {
while x != self.up[x] {
self.up[x] = self.up[self.up[x]];
x = self.up[x];
}
x
}
fn join(&mut self, mut rx: usize, mut ry: usize) -> usize {
if self.rank[rx] < self.rank[ry] {
std::mem::swap(&mut rx, &mut ry);
}
self.up[ry] = rx;
if self.rank[rx] == self.rank[ry] {
self.rank[rx] += 1;
}
rx
}
}