fn main() { let stdin = std::io::read_to_string(std::io::stdin().lock()).unwrap(); let mut stdin = stdin.split_ascii_whitespace(); let n: usize = stdin.next().unwrap().parse().unwrap(); let m: usize = stdin.next().unwrap().parse().unwrap(); let slimes: Vec<(u32, u32)> = (0..n) .map(|_| { ( stdin.next().unwrap().parse().unwrap(), stdin.next().unwrap().parse().unwrap(), ) }) .collect(); use std::io::Write; std::io::stdout() .lock() .write_all(output(solve(n, m, slimes)).as_bytes()) .unwrap(); } fn prepare(n: usize, slimes: Vec<(u32, u32)>) -> Vec> { let mut where_is = vec![Vec::new(); n + 1]; slimes .into_iter() .for_each(|(b, c)| where_is[c as usize].push(b)); where_is } fn solve(n: usize, m: usize, slimes: Vec<(u32, u32)>) -> u32 { let where_is = prepare(n, slimes); let mut ans = 0; let mut uf = mylib::UnionFind::new(m + 1); where_is.into_iter().for_each(|w| { w.windows(2).for_each(|w| { if uf.merge(w[0] as usize, w[1] as usize) { ans += 1 } }) }); ans } fn output(ans: u32) -> String { ans.to_string() + "\n" } mod mylib { pub struct UnionFind { count_groups: usize, root_of: Vec, size_of: Vec, } impl UnionFind { pub fn new(size: usize) -> UnionFind { UnionFind { count_groups: size, root_of: (0..size as u32).collect::>(), size_of: vec![1; size], } } fn search_true_root_of(&mut self, index: usize) -> usize { if self.root_of[index as usize] as usize != index { self.root_of[index as usize] = self.search_true_root_of(self.root_of[index as usize] as usize) as u32; } self.root_of[index as usize] as usize } pub fn is_connected_between(&mut self, a: usize, b: usize) -> bool { self.search_true_root_of(a) == self.search_true_root_of(b) } pub fn merge(&mut self, a: usize, b: usize) -> bool { if self.is_connected_between(a, b) { false } else { let (root_of_a, root_of_b) = (self.root_of[a as usize], self.root_of[b as usize]); self.size_of[root_of_a as usize] += self.size_of[root_of_b as usize]; self.root_of[root_of_b as usize] = self.root_of[root_of_a as usize]; true } } #[allow(unused)] pub fn size_of_group_of(&mut self, a: usize) -> usize { let root = self.search_true_root_of(a); self.size_of[root] as usize } #[allow(unused)] pub fn groups(&self) -> usize { self.count_groups } } }