Problem:
Score : 600 points

Problem Statement

Snuke's town has a subway system, consisting of N stations and M railway lines. The stations are numbered 1 through N. Each line is operated by a company. Each company has an identification number.

The i-th ( 1 \leq i \leq M ) line connects station p_i and q_i bidirectionally. There is no intermediate station. This line is operated by company c_i.

You can change trains at a station where multiple lines are available.

The fare system used in this subway system is a bit strange. When a passenger only uses lines that are operated by the same company, the fare is 1 yen (the currency of Japan). Whenever a passenger changes to a line that is operated by a different company from the current line, the passenger is charged an additional fare of 1 yen. In a case where a passenger who changed from some company A's line to another company's line changes to company A's line again, the additional fare is incurred again.

Snuke is now at station 1 and wants to travel to station N by subway. Find the minimum required fare.

Constraints

2 \leq N \leq 10^5

0 \leq M \leq 2×10^5

1 \leq p_i \leq N (1 \leq i \leq M)

1 \leq q_i \leq N (1 \leq i \leq M)

1 \leq c_i \leq 10^6 (1 \leq i \leq M)

p_i \neq q_i (1 \leq i \leq M)

Input

The input is given from Standard Input in the following format:

N M
p_1 q_1 c_1
:
p_M q_M c_M

Output

Print the minimum required fare. If it is impossible to get to station N by subway, print -1 instead.

Sample Input 1

3 3
1 2 1
2 3 1
3 1 2

Sample Output 1

1

Use company 1's lines: 1 → 2 → 3. The fare is 1 yen.

Sample Input 2

8 11
1 3 1
1 4 2
2 3 1
2 5 1
3 4 3
3 6 3
3 7 3
4 8 4
5 6 1
6 7 5
7 8 5

Sample Output 2

2

First, use company 1's lines: 1 → 3 → 2 → 5 → 6. Then, use company 5's lines: 6 → 7 → 8. The fare is 2 yen.

Sample Input 3

2 0

Sample Output 3

-1

Solution in Rust (1.15.1) (judge verdict: Accepted)

#[allow(unused_imports)]
use std::cmp::{min,max};
#[allow(unused_imports)]
use std::collections::BTreeMap;
#[allow(unused_imports)]
use std::ops::*;
#[allow(unused_imports)]
use std::collections::BinaryHeap;

fn main() {
    let (n,m) = {
        let xs = read_vec_i64();
        (xs[0] as usize, xs[1] as usize)
    };
    let (edges,companies) = {
        // (v,c) -> vec![next_v]
        let mut edges = BTreeMap::new();
        for i in 0..n {
            edges.insert((i,0), vec![i]);
        }
        let mut companies = vec![vec![]; n];
        for _ in 0..m {
            let xs = read_vec_i64();
            let (p,q,c) = ((xs[0]-1) as usize, (xs[1]-1) as usize, xs[2]);
            edges.entry((p,c)).or_insert(vec![q]).push(q);
            edges.entry((q,c)).or_insert(vec![p]).push(p);
            companies[p].push(c);
            companies[q].push(c);
        }
        (edges,companies)
    };
    // (v, c) -> d
    let mut dists = BTreeMap::new();
    //dists.insert((0,0),0);
    // (-d, v, c)
    let mut heap = BinaryHeap::new();
    heap.push((0,0,0));
    while ! heap.is_empty() {
        let (neg_d, v, c) = heap.pop().unwrap();
        let d = -neg_d;
        if let Some(&prev_d) = dists.get(&(v,c)) {
            if prev_d < d {
                continue;
            }
        }
        if c == 0 {
            for &company in &companies[v] {
                let next_d = d+1;
                match dists.get(&(v,company)) {
                    Some(&prev_d) => {
                        if prev_d <= next_d {
                            continue;
                        }
                    }
                    None => {
                    }
                }
                //println!("{},{} -> {}", v, e_c, next_d);
                dists.insert((v,company),next_d);
                heap.push((-next_d, v, company));
            }
        }
        else {
            for &c2 in &[0,c] {
                if let Some(es) = edges.get(&(v,c2)) {
                    for &next in es {
                        let next_d = d;
                        if let Some(&prev_d) = dists.get(&(next,c2)) {
                            if prev_d <= next_d {
                                continue;
                            }
                        }
                        //println!("{},{} -> {}", next, e_c, next_d);
                        dists.insert((next,c2), next_d);
                        heap.push((-next_d, next, c2));
                    }
                }
            }
        }
    }
    match dists.get(&(n-1,0)) {
        Some(&d) => {
            println!("{}", d);
        }
        None => {
            println!("-1");
        }
    }
}

#[allow(dead_code)]
fn read_line() -> String {
    let mut ret = String::new();
    std::io::stdin().read_line(&mut ret).ok();
    ret.pop();
    return ret;
}

#[allow(dead_code)]
fn read_i64() -> i64 {
    let ss = read_line();
    return ss.parse::<i64>().unwrap();
}

#[allow(dead_code)]
fn read_vec_i64() -> Vec<i64> {
    let mut res = vec![];
    let ss = read_line();
    for ts in ss.split_whitespace() {
        let x = ts.parse::<i64>().unwrap();
        res.push(x);
    }
    return res;
}

use std::fmt::Display;
#[allow(dead_code)]
fn write_vec<T: Display>(xs: &Vec<T>) {
    if xs.len() == 0 {
        println!("");
        return;
    }
    print!("{}", xs[0]);
    for i in 1..xs.len() {
        print!(" {}", xs[i]);
    }
    println!("");
}