pub mod spaghetti_source {

pub mod graph {

    pub mod articulation_point {
        use super::{Edge, Graph};
        use std::cmp::{max, min, Ordering};
        use std::collections::{BTreeSet, VecDeque};
        pub fn articulation_point(g: &Graph) -> (Vec<usize>, Vec<EdgeSet>) {
            let n = g.len();
            let mut low = vec![0 ; n];
            let mut num = vec![0 ; n];
            let mut art = BTreeSet::new();
            let mut bcomp = Vec::new();
            let mut s = VecDeque::new();
            for u in 0..n {
                if num[u] == 0 {
                    let mut time = 0;
                    visit(g, u, &mut art, &mut bcomp, &mut s, &mut num,
                          &mut low, &mut time);
                }
            }
            (art.into_iter().collect(), bcomp)
        }
        fn visit(g: &Graph, v: usize, art: &mut BTreeSet<usize>,
                 bcomp: &mut Vec<EdgeSet>, s: &mut VecDeque<Edge>,
                 num: &mut Vec<i32>, low: &mut Vec<i32>, time: &mut i32) {
            *time += 1;
            low[v] = *time;
            num[v] = *time;
            for e in &g[v] {
                let w = e.dst;
                if num[w] < num[v] { s.push_back(e.clone()); }
                if num[w] == 0 {
                    visit(g, w, art, bcomp, s, num, low, time);
                    low[v] = min(low[v], low[w]);
                    if (num[v] == 1 && num[w] != 2) ||
                           (num[v] != 1 && low[w] >= num[v]) {
                        art.insert(v);
                    }
                    if low[w] >= num[v] {
                        bcomp.push(EdgeSet::new());
                        while let Some(f) = s.pop_back() {
                            let n = bcomp.len();
                            let end = f.src == v && f.dst == w;
                            bcomp[n - 1].insert(UndirectEdge(f));
                            if end { break ; }
                        }
                    }
                } else { low[v] = min(low[v], num[w]); }
            }
        }
        pub type EdgeSet = BTreeSet<UndirectEdge>;
        #[derive(Eq, PartialEq, Debug)]
        pub struct UndirectEdge(Edge);
        impl UndirectEdge {
            pub fn min_v(&self) -> usize { min(self.0.src, self.0.dst) }
            pub fn max_v(&self) -> usize { max(self.0.src, self.0.dst) }
        }
        impl Ord for UndirectEdge {
            fn cmp(&self, other: &UndirectEdge) -> Ordering {
                self.min_v().cmp(&other.min_v()).then_with(||
                                                               self.max_v().cmp(&other.max_v()))
            }
        }
        impl PartialOrd for UndirectEdge {
            fn partial_cmp(&self, other: &UndirectEdge) -> Option<Ordering> {
                Some(self.cmp(other))
            }
        }
    }
    pub mod bridge {
        use super::{Edge, Graph};
        use std::collections::VecDeque;
        pub fn bridge(g: &Graph) -> (Vec<Edge>, Vec<Vec<usize>>) {
            let n = g.len();
            let mut num = vec![0 ; n];
            let mut in_stq = vec![false ; n];
            let mut roots = VecDeque::new();
            let mut stq = VecDeque::new();
            let mut time = 0;
            let mut brdg = VecDeque::new();
            let mut tecomp = Vec::new();
            for u in 0..n {
                if num[u] == 0 {
                    visit(g, u, n, &mut brdg, &mut tecomp, &mut roots,
                          &mut stq, &mut in_stq, &mut num, &mut time);
                    brdg.pop_back();
                }
            }
            (brdg.into_iter().collect(), tecomp)
        }
        fn visit(g: &Graph, v: usize, u: usize, brdg: &mut VecDeque<Edge>,
                 tecomp: &mut Vec<Vec<usize>>, roots: &mut VecDeque<usize>,
                 stq: &mut VecDeque<usize>, in_stq: &mut Vec<bool>,
                 num: &mut Vec<usize>, time: &mut usize) {
            *time += 1;
            num[v] = *time;
            stq.push_back(v);
            in_stq[v] = true;
            roots.push_back(v);
            for e in &g[v] {
                let w = e.dst;
                if num[w] == 0 {
                    visit(g, w, v, brdg, tecomp, roots, stq, in_stq, num,
                          time);
                } else if u != w && in_stq[w] {
                    while num[*roots.back().unwrap()] > num[w] {
                        roots.pop_back();
                    }
                }
            }
            if v == *roots.back().unwrap() {
                brdg.push_back(Edge{src: u, dst: v, weight: 0,});
                tecomp.push(Vec::new());
                loop  {
                    let w = stq.pop_back().unwrap();
                    in_stq[w] = false;
                    let n = tecomp.len();
                    tecomp[n - 1].push(w);
                    if v == w { break ; }
                }
                roots.pop_back();
            }
        }
    }
    pub mod dijkstra {
        use super::{Graph, Weight};
        use std::cmp::Ordering;
        use std::collections::{BinaryHeap, HashMap};
        pub fn shortest_path(g: &Graph, s: usize)
         -> (HashMap<usize, Weight>, HashMap<usize, usize>) {
            let mut dist = HashMap::new();
            let mut prev = HashMap::new();
            let mut queue = BinaryHeap::new();
            dist.insert(s, 0);
            queue.push(State{cost: 0, pos: s, last_pos: s,});
            while let Some(State { cost, pos, last_pos }) = queue.pop() {
                if prev.contains_key(&pos) { continue ; }
                prev.insert(pos, last_pos);
                for e in &g[pos] {
                    let npos = e.dst;
                    let ncost = cost + e.weight;
                    if dist.get(&npos).unwrap_or(&Weight::max_value()) >
                           &ncost {
                        dist.insert(npos, ncost);
                        queue.push(State{cost: ncost,
                                         pos: npos,
                                         last_pos: pos,});
                    }
                }
            }
            (dist, prev)
        }
        pub fn build_path(prev: &HashMap<usize, usize>, t: usize)
         -> Option<Vec<usize>> {
            let mut path = Vec::new();
            let mut u = t;
            loop  {
                path.push(u);
                match prev.get(&u) {
                    Some(&next) if u == next => { break ; }
                    Some(&next) => { u = next; }
                    None => { return None; }
                };
            }
            path.reverse();
            Some(path)
        }
        #[derive(Copy, Clone, Eq, PartialEq)]
        struct State {
            cost: Weight,
            pos: usize,
            last_pos: usize,
        }
        impl Ord for State {
            fn cmp(&self, other: &State) -> Ordering {
                other.cost.cmp(&self.cost).then_with(||
                                                         self.pos.cmp(&other.pos)).then_with(||
                                                                                                 self.last_pos.cmp(&other.last_pos))
            }
        }
        impl PartialOrd for State {
            fn partial_cmp(&self, other: &State) -> Option<Ordering> {
                Some(self.cmp(other))
            }
        }
    }
    pub mod strongly_connected_components {
        use super::Graph;
        use std::cmp::min;
        use std::collections::VecDeque;
        fn visit(g: &Graph, v: usize, scc: &mut Vec<Vec<usize>>,
                 stk: &mut VecDeque<usize>, in_stk: &mut Vec<bool>,
                 low: &mut Vec<usize>, num: &mut Vec<usize>,
                 time: &mut usize) {
            *time += 1;
            low[v] = *time;
            num[v] = *time;
            stk.push_back(v);
            in_stk[v] = true;
            for e in &g[v] {
                let w = e.dst;
                if num[w] == 0 {
                    visit(g, w, scc, stk, in_stk, low, num, time);
                    low[v] = min(low[v], low[w]);
                } else if in_stk[w] { low[v] = min(low[v], num[w]); }
            }
            if low[v] == num[v] {
                scc.push(Vec::new());
                loop  {
                    let w = stk.pop_back().unwrap();
                    in_stk[w] = false;
                    let n = scc.len();
                    scc[n - 1].push(w);
                    if v == w { break ; }
                }
            }
        }
        pub fn strongly_connected_component(g: &Graph) -> Vec<Vec<usize>> {
            let n = g.len();
            let mut scc = Vec::new();
            let mut num = vec![0 ; n];
            let mut low = vec![0 ; n];
            let mut stk = VecDeque::new();
            let mut in_stk = vec![false ; n];
            let mut time = 0;
            for u in 0..n {
                if num[u] == 0 {
                    visit(g, u, &mut scc, &mut stk, &mut in_stk, &mut low,
                          &mut num, &mut time);
                }
            }
            scc
        }
    }
    use std::cmp::Ordering;
    pub type Weight = i64;
    #[derive(Eq, PartialEq, Debug, Clone)]
    pub struct Edge {
        pub src: usize,
        pub dst: usize,
        pub weight: Weight,
    }
    impl Ord for Edge {
        fn cmp(&self, other: &Edge) -> Ordering {
            self.weight.cmp(&other.weight).reverse().then_with(||
                                                                   self.src.cmp(&other.src)).then_with(||
                                                                                                           self.dst.cmp(&other.dst))
        }
    }
    impl PartialOrd for Edge {
        fn partial_cmp(&self, other: &Edge) -> Option<Ordering> {
            Some(self.cmp(other))
        }
    }
    pub type Edges = Vec<Edge>;
    pub type Graph = Vec<Edges>;
    pub type Array = Vec<Weight>;
    pub type Matrix = Vec<Array>;
}
#[cfg(test)]
mod tests {
    #[test]
    fn it_works() { assert_eq!(2 + 2 , 4); }
}
}


use spaghetti_source::graph::dijkstra::shortest_path;
use spaghetti_source::graph::{Edge, Edges};

fn main() {
    let (n, m, s) = readln();
    let mut g = vec![Edges::new(); n];
    for _ in 0..m {
        let (x, y, d): (usize, _, _) = readln();
        g[x].push(Edge {
            src: x,
            dst: y,
            weight: d,
        });
    }
    let (dist, _) = shortest_path(&g, s);
    for u in 0..n {
        match dist.get(&u) {
            Some(c) => {
                println!("{}", c);
            }
            None => {
                println!("INF");
            }
        }
    }
}

pub trait FromLn {
    fn fromln(s: &str) -> Self;
}
pub fn readln<T: FromLn>() -> T {
    let mut buf = String::new();
    let _ = ::std::io::stdin().read_line(&mut buf).unwrap();
    T::fromln(buf.trim())
}
pub fn readlns<T: FromLn>(n: usize) -> Vec<T> {
    let mut vs = vec![];
    for _ in 0..n {
        vs.push(readln());
    }
    vs
}
macro_rules! fromln_primitives {
    ($($t:ty),*) => { $(
        impl FromLn for $t {
            fn fromln(s: &str) -> $t {
                s.parse().unwrap()
            }
        }
    )* }
}
fromln_primitives!(String, bool, f32, f64, isize, i8, i16, i32, i64, usize, u8, u16, u32, u64);
impl<T> FromLn for Vec<T>
where
    T: FromLn,
{
    fn fromln(s: &str) -> Vec<T> {
        s.split_whitespace().map(T::fromln).collect()
    }
}
impl FromLn for Vec<char> {
    fn fromln(s: &str) -> Vec<char> {
        s.chars().collect()
    }
}
macro_rules! fromln_tuple {
    ($($t:ident),*) => {
        impl<$($t),*> FromLn for ($($t),*) where $($t: FromLn),* {
            fn fromln(s: &str) -> ($($t),*) {
                let mut it = s.split_whitespace();
                let t = ($($t::fromln(it.next().unwrap())),*);
                assert_eq!(it.next(), None);
                t
            }
        }
    }
}
fromln_tuple!(A, B);
fromln_tuple!(A, B, C);
fromln_tuple!(A, B, C, D);
fromln_tuple!(A, B, C, D, E);
fromln_tuple!(A, B, C, D, E, F);



Measured memory_bytes: 39440