Problem:
ヘビの JOI 君 (Snake JOI)

問題

ヘビの JOI 君は，ある大きな屋敷に迷い込んでしまった．屋敷の住人に見つかる前に，屋敷を脱出しなければならない．

この屋敷には部屋が N 個あり，1, 2, ..., N の番号が付けられている．また，廊下が M 本あり，i 本目の廊下 (1 ≦ i ≦ M) は部屋 Ai と部屋 Bi を結んでいる．JOI 君はこれらの廊下をどちらの向きにも通ることができ，廊下 i を通るのには Di 分かかる．部屋と部屋の間を廊下を通る以外の手段で移動する方法はない．

この屋敷の部屋の温度はそれぞれ一定に調節されており，JOI 君にとって寒すぎるか，快適であるか，暑すぎるかである．JOI 君は，急な温度変化に対応できないため，最後に寒すぎる部屋を出てから X 分未満のうちに暑すぎる部屋に入ることはできない．同様に，最後に暑すぎる部屋を出てから X 分未満のうちに寒すぎる部屋に入ることもできない．

JOI 君は，移動中に部屋に入るとすぐに部屋から出なければならない．また，廊下の途中で引き返したり，廊下 i を Di 分より長い時間かけて通ることもできない．ただし，一度訪れた部屋にもう一度入ることや，一度使った廊下をもう一度使うことは許される．

JOI 君は現在部屋 1 にいる．この部屋は JOI 君にとって寒すぎる．JOI 君は屋敷の出口のある部屋 N に入ると，屋敷から脱出できる．

JOI 君が屋敷から脱出するのにかかる最短の時間を求めよ．

入力

入力は 1 + N + M 行からなる．

1 行目には，3 個の整数 N, M, X (2 ≦ N ≦ 10000, 1 ≦ M ≦ 20000, 1 ≦ X ≦ 200) が空白を区切りとして書かれている．これは，屋敷に N 個の部屋と M 本の廊下があり，JOI 君が温度変化に対応するのに X 分かかることを表す．

続く N 行のうちの i 行目 (1 ≦ i ≦ N) には，部屋 i の温度を表す整数 Ti (0 ≦ Ti ≦ 2) が書かれている．JOI 君にとって部屋 i は，Ti = 0 のとき寒すぎ，Ti = 1 のとき快適であり，Ti = 2 のとき暑すぎる．T1 = 0 であることが保証されている．

続く M 行のうちの j 行目 (1 ≦ j ≦ M) には，3 個の整数 Aj, Bj, Dj (1 ≦ Aj < Bj ≦ N, 1 ≦ Dj ≦ 200) が空白を区切りとして書かれている．これは，廊下 j が部屋 Aj と部屋 Bj を結んでおり，通るのに Dj 分かかることを表す．同じ部屋の組を結ぶ廊下が複数ある可能性があることに注意せよ．

与えられる入力データでは，JOI 君が屋敷から脱出できることは保証されている．

出力

JOI 君が屋敷から脱出するのに最短で何分かかるかを表す整数を 1 行で出力せよ．

入出力例

入力例 1

8 10 4
0
1
1
2
1
1
2
0
1 2 1
1 3 1
2 3 3
2 4 5
3 4 1
4 5 1
5 6 1
5 8 1
1 7 2
7 8 2

出力例 1

9

入力例 2

15 25 4
0
1
1
0
2
1
0
1
1
2
0
0
1
0
1
8 11 1
7 10 1
12 14 1
3 8 1
1 5 1
3 9 1
3 8 1
1 5 1
6 15 1
11 12 1
2 14 1
7 10 1
11 12 1
5 13 1
2 8 1
1 4 1
2 11 1
5 6 1
1 13 1
6 12 1
5 10 1
9 13 1
4 10 1
3 12 1
7 13 1

出力例 2

6

入力例 1 では，部屋を 1 → 2 → 3 → 4 → 5 → 6 → 5 → 8 の順に移動するのが最短となる．

入力例 2 では，いくつかの部屋の組 (たとえば部屋 1 と部屋 5) を結ぶ廊下が複数ある．

情報オリンピック日本委員会作 『第 16 回日本情報オリンピック JOI 2016/2017 予選競技課題』

Solution in Rust (judge verdict: Accepted)

extern crate core;

use std::fmt;
use std::cmp::{Ordering, min, max};
use std::fmt::{Display, Error, Formatter};
use std::collections::{VecDeque, BinaryHeap, BTreeMap};
use std::f32::MAX;
use ::Temperature::{Comfort, Cold, Hot};

fn show<T: Display>(vec: &Vec<T>) {
    if vec.is_empty() {
        println!("[]");
    }else {
        print!("[{}", vec[0]);
        for i in 1 .. vec.len() {
            print!(", {}", vec[i]);
        }
        println!("]");
    }
}
macro_rules! read_line{
    () => {{
        let mut line = String::new();
        std::io::stdin().read_line(&mut line).ok();
        line
    }};
    (delimiter: ' ') => {
        read_line!().split_whitespace().map(|x|x.to_string()).collect::<Vec<_>>()
    };
    (delimiter: $p:expr) => {
        read_line!().split($p).map(|x|x.to_string()).collect::<Vec<_>>()
    };
    (' ') => {
        read_line!(delimiter: ' ')
    };
    ($delimiter:expr) => {
        read_line!(delimiter: $delimiter)
    };
    (' '; $ty:ty) => {
        read_line!().split_whitespace().map(|x|x.parse::<$ty>().ok().unwrap()).collect::<Vec<$ty>>()
    };
    ($delimiter:expr; $ty:ty) => {
        read_line!($delimiter).into_iter().map(|x|x.parse::<$ty>().ok().unwrap()).collect::<Vec<$ty>>()
    };
}
macro_rules! let_all {
    ($($n:ident:$t:ty),*) => {
        let line = read_line!(delimiter: ' ');
        let mut iter = line.iter();
        $(let $n:$t = iter.next().unwrap().parse().ok().unwrap();)*
    };
}
#[derive(Copy, Clone)]
enum Temperature {
    Comfort, Hot, Cold
}
#[derive(Copy, Clone)]
struct Corridor {
    to: usize, time: usize,
}
struct KeyWithValue<K, V> {
    key: K, value: V
}
impl <K, V> KeyWithValue<K, V> {
    fn new(key: K, value: V) -> KeyWithValue<K, V> {
        KeyWithValue{key: key, value: value}
    }
}
impl <K: Ord, V> Ord for KeyWithValue<K, V> {
    fn cmp(&self, other: &Self) -> Ordering {
        other.key.cmp(&self.key)
    }
}
impl <K: PartialOrd, V> PartialOrd for KeyWithValue<K, V> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        other.key.partial_cmp(&self.key)
    }
}
impl <K: PartialEq, V> PartialEq for KeyWithValue<K, V> {
    fn eq(&self, other: &Self) -> bool {
        self.key.eq(&other.key)
    }
}
impl <K: PartialEq, V> Eq for KeyWithValue<K, V>{}
fn main() {
    let_all!(n: usize, m: usize, x: usize);
    let mut rooms = Vec::with_capacity(n);
    for _ in 0 .. n {
        let_all!(t: usize);
        match t {
            0 => rooms.push(Cold),
            1 => rooms.push(Comfort),
            2 => rooms.push(Hot),
            _ => unreachable!(),
        }
    }
    let rooms = rooms;
    let mut corridors: Vec<Vec<Corridor>> = vec![Vec::new(); n];
    for _ in 0 .. m {
        let_all!(a: usize, b: usize, d: usize);
        corridors[a - 1].push(Corridor{to: b - 1, time: d});
        corridors[b - 1].push(Corridor{to: a - 1, time: d});
    }
    let corridors = corridors;
    let mut min_time: Vec<Vec<i64>> = vec![vec![std::i64::MAX; 2 * x + 1]; n];
    let mut heap: BinaryHeap<KeyWithValue<i64, (usize, usize)>> = BinaryHeap::new();
    min_time[0][0] = 0_i64;
    heap.push(KeyWithValue::new(0_i64, (0 as usize, 0 as usize)));
    while let Some(KeyWithValue{key: time, value: (room, x_time)}) = heap.pop() {
        if room == n - 1 {
            println!("{}", time);
            break
        }
        if min_time[room][x_time] == time {
            for &Corridor{to: next, time: distance} in &corridors[room] {
                match rooms[next] {
                    Hot if x_time + distance >= x => {
                        if min_time[next][x * 2] > time + distance as i64 {
                            min_time[next][x * 2] = time + distance as i64;
                            heap.push(KeyWithValue::new(min_time[next][x * 2], (next, x * 2)));
                        }
                    }
                    Cold if x_time <= distance + x => {
                        if min_time[next][0] > time + distance as i64 {
                            min_time[next][0] = time + distance as i64;
                            heap.push(KeyWithValue::new(min_time[next][0], (next, 0)));
                        }
                    }
                    Comfort => {
                        if x_time <= x {
                            let next_x = min(x_time + distance, x);
                            if min_time[next][next_x] > time + distance as i64 {
                                min_time[next][next_x] = time + distance as i64;
                                heap.push(KeyWithValue::new(min_time[next][next_x], (next, next_x)));
                            }
                        }else {
                            let next_x = if x_time <= distance + x {x} else {x_time - distance};
                            if min_time[next][next_x] > time + distance as i64 {
                                min_time[next][next_x] = time + distance as i64;
                                heap.push(KeyWithValue::new(min_time[next][next_x], (next, next_x)));
                            }
                        }
                    }
                    _ => {}
                }
            }
        }
    }
}