// Reference: https://github.com/matajupi/matajupilib_snippets //  ∧_∧ // (。・ﻌ・。)つ━☆・*。 // ⊂   ノ    ✧・゜+. //  しーJ   °。+ *´¨) //          .· ´¸.·´¨) ¸.·¨) //           (¸.·´ ✧(¸.·’*✧ #![allow(dead_code, unused_imports, unused_macros, unexpected_cfgs)] #[macro_use] mod matajupilib { use crate::*; // ============================================================================= // Macros // ============================================================================= macro_rules! chmin { ($a:expr, $b:expr) => { if $b < $a { $a = $b; true } else { false } }; } macro_rules! chmax { ($a:expr, $b:expr) => { if $b > $a { $a = $b; true } else { false } }; } macro_rules! r { ($num:expr, $den:expr) => { Rational64::new($num, $den) }; } /// Debug print macro (Only works in Debug builds; ignored in Release builds) macro_rules! d { ($($a:expr),* $(,)?) => { #[cfg(debug_assertions)] eprintln!(concat!($(stringify!($a), " = {:?}, "),*), $($a),*); }; } // ============================================================================= // Constants & Type Aliases // ============================================================================= pub const IINF: i64 = 4_000_000_000_000_000_000; pub const UINF: usize = 1_000_000_000; pub const DX4: [i64; 4] = [1, 0, -1, 0]; pub const DY4: [i64; 4] = [0, 1, 0, -1]; pub const DX8: [i64; 8] = [1, 1, 0, -1, -1, -1, 0, 1]; pub const DY8: [i64; 8] = [0, 1, 1, 1, 0, -1, -1, -1]; pub type MinHeap = BinaryHeap>; pub type MaxHeap = BinaryHeap; // ============================================================================= // Type Cast Traits // ============================================================================= macro_rules! define_cast_trait { ($trait_name:ident, $fn_name:ident, $target_type:ty, $($source_type:ty),*) => { pub trait $trait_name { fn $fn_name(self) -> $target_type; } $( impl $trait_name for $source_type { #[inline] fn $fn_name(self) -> $target_type { self as $target_type } } )* }; } define_cast_trait!( AsUsize, u, usize, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, char, f32, f64 ); define_cast_trait!( AsI64, i, i64, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, char, f32, f64 ); define_cast_trait!( AsF64, f, f64, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64 ); define_cast_trait!( AsI128, l, i128, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, char, f32, f64 ); pub trait AsMint { fn m(self) -> Mint; } impl AsMint for T where T: Into, { #[inline] fn m(self) -> Mint { self.into() } } // ============================================================================= // Helper Traits // ============================================================================= pub trait Unwrapper { fn uw(self) -> T; fn uwor(self, default: T) -> T; fn uworelse(self, f: F) -> T where F: FnOnce() -> T; } impl Unwrapper for Option { #[inline] fn uw(self) -> T { self.expect("None value") } #[inline] fn uwor(self, default: T) -> T { self.unwrap_or(default) } #[inline] fn uworelse(self, f: F) -> T where F: FnOnce() -> T, { self.unwrap_or_else(f) } } impl Unwrapper for Result { #[inline] fn uw(self) -> T { self.expect("Err value") } #[inline] fn uwor(self, default: T) -> T { self.unwrap_or(default) } #[inline] fn uworelse(self, f: F) -> T where F: FnOnce() -> T, { self.unwrap_or_else(|_| f()) } } pub trait ShortNumOps: Sized { fn sadd(self, rhs: Self) -> Self; fn ssub(self, rhs: Self) -> Self; fn smul(self, rhs: Self) -> Self; fn oadd(self, rhs: Self) -> (Self, bool); fn osub(self, rhs: Self) -> (Self, bool); fn omul(self, rhs: Self) -> (Self, bool); } macro_rules! impl_short_num_ops { ($($t:ty),*) => { $( impl ShortNumOps for $t { #[inline] fn sadd(self, rhs: Self) -> Self { self.saturating_add(rhs) } #[inline] fn ssub(self, rhs: Self) -> Self { self.saturating_sub(rhs) } #[inline] fn smul(self, rhs: Self) -> Self { self.saturating_mul(rhs) } #[inline] fn oadd(self, rhs: Self) -> (Self, bool) { self.overflowing_add(rhs) } #[inline] fn osub(self, rhs: Self) -> (Self, bool) { self.overflowing_sub(rhs) } #[inline] fn omul(self, rhs: Self) -> (Self, bool) { self.overflowing_mul(rhs) } } )* }; } impl_short_num_ops!( i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize ); pub trait MapExt { fn orins(&mut self, key: K, default: V) -> &mut V; fn ordef(&mut self, key: K) -> &mut V where V: Default; fn orwith V>(&mut self, key: K, f: F) -> &mut V; } impl MapExt for HashMap { #[inline] fn orins(&mut self, key: K, default: V) -> &mut V { self.entry(key).or_insert(default) } #[inline] fn ordef(&mut self, key: K) -> &mut V where V: Default, { self.entry(key).or_default() } #[inline] fn orwith V>(&mut self, key: K, f: F) -> &mut V { self.entry(key).or_insert_with(f) } } impl MapExt for BTreeMap { #[inline] fn orins(&mut self, key: K, default: V) -> &mut V { self.entry(key).or_insert(default) } #[inline] fn ordef(&mut self, key: K) -> &mut V where V: Default, { self.entry(key).or_default() } #[inline] fn orwith V>(&mut self, key: K, f: F) -> &mut V { self.entry(key).or_insert_with(f) } } pub trait StrExt { fn cv(&self) -> Vec; } impl StrExt for str { #[inline] fn cv(&self) -> Vec { self.chars().collect() } } // ============================================================================= // Iterator Extensions // ============================================================================= pub trait IntoIterExt: IntoIterator + Sized { #[inline] fn iit(self) -> Self::IntoIter { self.into_iter() } #[inline] fn col>(self) -> B { self.into_iter().collect() } #[inline] fn colv(self) -> Vec { self.into_iter().collect() } } impl IntoIterExt for T {} pub trait IterExt { #[inline] fn it(&self) -> <&Self as IntoIterator>::IntoIter where for<'a> &'a Self: IntoIterator, { self.into_iter() } #[inline] fn itm(&mut self) -> <&mut Self as IntoIterator>::IntoIter where for<'a> &'a mut Self: IntoIterator, { self.into_iter() } } impl IterExt for T {} // ============================================================================= // Scanner // ============================================================================= use std::io::Read; pub struct Scanner { iter: std::vec::IntoIter, } impl Scanner { pub fn new() -> Self { let mut s = String::new(); std::io::stdin().read_to_string(&mut s).unwrap(); Self { iter: s.split_whitespace().map(|s| s.to_string()) .collect::>().into_iter(), } } pub fn n(&mut self) -> T { self.iter.next().unwrap().parse().ok().unwrap() } pub fn ns(&mut self, n: usize) -> Vec { (0..n).map(|_| self.n()).collect() } } } // ============================================================================= // Imports & Main // ============================================================================= // AtCoder // use amplify::*; // use omniswap::swap; // use ordered_float::OrderedFloat as OF; // use rand::Rng; // use superslice::Ext; // yukicoder use ::num::{integer::*, rational::*, *}; use itertools::Itertools; use ac_library::*; use proconio::{marker::*, *}; // Other use std::cmp::{Reverse as R, *}; use std::collections::{HashMap as Map, HashSet as Set, *}; use std::f64::consts::*; use std::hash::{BuildHasher, Hash}; use std::mem::*; use std::ops::{Range, RangeBounds}; // AtCoder, yukicoder use ac_library::ModInt998244353 as Mint; // Other // type Mint = i64; use matajupilib::*; fn solve() { input! { n: usize, k: usize, xy: [(Usize1, Usize1); k], }; let mut to = (0..n).colv(); for i in 0..k { let (x, y) = xy[i]; let t = to[x]; to[x] = to[y]; to[y] = t; } let mut vis = vec![false; n]; let mut res = 1; for i in 0..n { if !vis[i] { let mut cnt = 0; let mut cur = i; while !vis[cur] { vis[cur] = true; cur = to[cur]; cnt += 1; } res = lcm(res, cnt); } } println!("{}", res); } fn main() { let t = 1; for _ in 0..t { solve(); } }