fn main() { let mut io = IO::new(); input! { from io, n: usize, m: usize, mut edges: [(Usize1, Usize1); m] } assert!(2 <= n && n <= 100_000); assert!(m <= 200_000); for &(u, v) in &edges { assert!(u < n); assert!(v < n); assert_ne!(u, v); } edges.sort(); edges.dedup(); if edges.len() != m { eprintln!("多重辺あり") } let mut scc = SccGraph::new(n); for &(u, v) in &edges { scc.add_edge(u, v); } let grouping = scc.scc(); let n_group = grouping.len(); if n_group == 1 { io.println(0); return; } let mut ids = vec![0; n]; for (g, l) in grouping.iter().enumerate() { for &v in l.iter() { ids[v] = g; } } let mut new_in = vec![0; n_group]; let mut new_out = vec![0; n_group]; for &(u, v) in &edges { if ids[u] != ids[v] { new_out[ids[u]] += 1; new_in[ids[v]] += 1; } } let ans = new_in .iter() .filter(|&&x| x == 0) .count() .max(new_out.iter().filter(|&&x| x == 0).count()); io.println(ans); } use scc::SccGraph; pub mod scc { use crate::internal_scc; pub struct SccGraph { internal: internal_scc::SccGraph, } impl SccGraph { pub fn new(n: usize) -> Self { SccGraph { internal: internal_scc::SccGraph::new(n), } } pub fn add_edge(&mut self, from: usize, to: usize) { let n = self.internal.num_vertices(); assert!(from < n); assert!(to < n); self.internal.add_edge(from, to); } pub fn scc(&self) -> Vec> { self.internal.scc() } } } pub mod internal_scc { pub struct Csr { start: Vec, elist: Vec, } impl Csr where E: Copy, { pub fn new(n: usize, edges: &[(usize, E)], init: E) -> Self { let mut csr = Csr { start: vec![0; n + 1], elist: vec![init; edges.len()], }; for e in edges.iter() { csr.start[e.0 + 1] += 1; } for i in 1..=n { csr.start[i] += csr.start[i - 1]; } let mut counter = csr.start.clone(); for e in edges.iter() { csr.elist[counter[e.0]] = e.1; counter[e.0] += 1; } csr } } #[derive(Copy, Clone)] struct _Edge { to: usize, } /// Reference: /// R. Tarjan, /// Depth-First Search and Linear Graph Algorithms pub struct SccGraph { n: usize, edges: Vec<(usize, _Edge)>, } impl SccGraph { pub fn new(n: usize) -> Self { SccGraph { n, edges: vec![] } } pub fn num_vertices(&self) -> usize { self.n } pub fn add_edge(&mut self, from: usize, to: usize) { self.edges.push((from, _Edge { to })); } /// return pair of (# of scc, scc id) pub fn scc_ids(&self) -> (usize, Vec) { // In C++ ac-library, this function is implemented by using recursive lambda functions. // Instead, we use fn and struct for capturing environments. struct _Env { g: Csr<_Edge>, now_ord: usize, group_num: usize, visited: Vec, low: Vec, ord: Vec>, ids: Vec, } let mut env = _Env { g: Csr::new(self.n, &self.edges, _Edge { to: 0 }), now_ord: 0, group_num: 0, visited: Vec::with_capacity(self.n), low: vec![0; self.n], ord: vec![None; self.n], ids: vec![0; self.n], }; fn dfs(v: usize, n: usize, env: &mut _Env) { env.low[v] = env.now_ord; env.ord[v] = Some(env.now_ord); env.now_ord += 1; env.visited.push(v); for i in env.g.start[v]..env.g.start[v + 1] { let to = env.g.elist[i].to; if let Some(x) = env.ord[to] { env.low[v] = std::cmp::min(env.low[v], x); } else { dfs(to, n, env); env.low[v] = std::cmp::min(env.low[v], env.low[to]); } } if env.low[v] == env.ord[v].unwrap() { loop { let u = *env.visited.last().unwrap(); env.visited.pop(); env.ord[u] = Some(n); env.ids[u] = env.group_num; if u == v { break; } } env.group_num += 1; } } for i in 0..self.n { if env.ord[i].is_none() { dfs(i, self.n, &mut env); } } for x in env.ids.iter_mut() { *x = env.group_num - 1 - *x; } (env.group_num, env.ids) } pub fn scc(&self) -> Vec> { let ids = self.scc_ids(); let group_num = ids.0; let mut counts = vec![0usize; group_num]; for &x in ids.1.iter() { counts[x] += 1; } let mut groups: Vec> = (0..ids.0).map(|_| vec![]).collect(); for i in 0..group_num { groups[i].reserve(counts[i]); } for i in 0..self.n { groups[ids.1[i]].push(i); } groups } } } // ------------ io module start ------------ use std::io::{stdout, BufWriter, Read, StdoutLock, Write}; pub struct IO { iter: std::str::SplitAsciiWhitespace<'static>, buf: BufWriter>, } impl IO { pub fn new() -> Self { let mut input = String::new(); std::io::stdin().read_to_string(&mut input).unwrap(); let input = Box::leak(input.into_boxed_str()); let out = Box::new(stdout()); IO { iter: input.split_ascii_whitespace(), buf: BufWriter::new(Box::leak(out).lock()), } } fn scan_str(&mut self) -> &'static str { self.iter.next().unwrap() } pub fn scan(&mut self) -> ::Output { ::scan(self) } pub fn scan_vec(&mut self, n: usize) -> Vec<::Output> { (0..n).map(|_| self.scan::()).collect() } pub fn print(&mut self, x: T) { ::print(self, x); } pub fn println(&mut self, x: T) { self.print(x); self.print("\n"); } pub fn iterln>(&mut self, iter: I, delim: &str) { let mut iter = iter.into_iter(); if let Some(v) = iter.next() { self.print(v); for v in iter { self.print(delim); self.print(v); } } self.print("\n"); } pub fn flush(&mut self) { self.buf.flush().unwrap(); } } impl Default for IO { fn default() -> Self { Self::new() } } pub trait Scan { type Output; fn scan(io: &mut IO) -> Self::Output; } macro_rules! impl_scan { ($($t:tt),*) => { $( impl Scan for $t { type Output = Self; fn scan(s: &mut IO) -> Self::Output { s.scan_str().parse().unwrap() } } )* }; } impl_scan!(i16, i32, i64, isize, u16, u32, u64, usize, String, f32, f64); impl Scan for char { type Output = char; fn scan(s: &mut IO) -> Self::Output { s.scan_str().chars().next().unwrap() } } pub enum Bytes {} impl Scan for Bytes { type Output = &'static [u8]; fn scan(s: &mut IO) -> Self::Output { s.scan_str().as_bytes() } } pub enum Chars {} impl Scan for Chars { type Output = Vec; fn scan(s: &mut IO) -> Self::Output { s.scan_str().chars().collect() } } pub enum Usize1 {} impl Scan for Usize1 { type Output = usize; fn scan(s: &mut IO) -> Self::Output { s.scan::().wrapping_sub(1) } } impl Scan for (T, U) { type Output = (T::Output, U::Output); fn scan(s: &mut IO) -> Self::Output { (T::scan(s), U::scan(s)) } } impl Scan for (T, U, V) { type Output = (T::Output, U::Output, V::Output); fn scan(s: &mut IO) -> Self::Output { (T::scan(s), U::scan(s), V::scan(s)) } } impl Scan for (T, U, V, W) { type Output = (T::Output, U::Output, V::Output, W::Output); fn scan(s: &mut IO) -> Self::Output { (T::scan(s), U::scan(s), V::scan(s), W::scan(s)) } } pub trait Print { fn print(w: &mut IO, x: Self); } macro_rules! impl_print_int { ($($t:ty),*) => { $( impl Print for $t { fn print(w: &mut IO, x: Self) { w.buf.write_all(x.to_string().as_bytes()).unwrap(); } } )* }; } impl_print_int!(i16, i32, i64, isize, u16, u32, u64, usize, f32, f64); impl Print for u8 { fn print(w: &mut IO, x: Self) { w.buf.write_all(&[x]).unwrap(); } } impl Print for &[u8] { fn print(w: &mut IO, x: Self) { w.buf.write_all(x).unwrap(); } } impl Print for &str { fn print(w: &mut IO, x: Self) { w.print(x.as_bytes()); } } impl Print for String { fn print(w: &mut IO, x: Self) { w.print(x.as_bytes()); } } impl Print for &T { fn print(w: &mut IO, x: Self) { w.print(*x); } } impl Print for (T, U) { fn print(w: &mut IO, (x, y): Self) { w.print(x); w.print(" "); w.print(y); } } impl Print for (T, U, V) { fn print(w: &mut IO, (x, y, z): Self) { w.print(x); w.print(" "); w.print(y); w.print(" "); w.print(z); } } pub mod neboccoio_macro { #[macro_export] macro_rules! input { (@start $io:tt @read @rest) => {}; (@start $io:tt @read @rest, $($rest: tt)*) => { input!(@start $io @read @rest $($rest)*) }; (@start $io:tt @read @rest mut $($rest:tt)*) => { input!(@start $io @read @mut [mut] @rest $($rest)*) }; (@start $io:tt @read @rest $($rest:tt)*) => { input!(@start $io @read @mut [] @rest $($rest)*) }; (@start $io:tt @read @mut [$($mut:tt)?] @rest $var:tt: [[$kind:tt; $len2: expr]; $len1:expr] $($rest:tt)*) => { let $($mut)* $var = (0..$len1).map(|_| $io.scan_vec::<$kind>($len2)).collect::<<$kind as Scan>::Output>(); input!(@start $io @read @rest $($rest)*) }; (@start $io:tt @read @mut [$($mut:tt)?] @rest $var:tt: [$kind:tt; $len:expr] $($rest:tt)*) => { let $($mut)* $var = $io.scan_vec::<$kind>($len); input!(@start $io @read @rest $($rest)*) }; (@start $io:tt @read @mut [$($mut:tt)?] @rest $var:tt: $kind:tt $($rest:tt)*) => { let $($mut)* $var = $io.scan::<$kind>(); input!(@start $io @read @rest $($rest)*) }; (from $io:tt $($rest:tt)*) => { input!(@start $io @read @rest $($rest)*) }; } } // ------------ io module end ------------