def get_i() #空白区切の入力を数値(整数)の配列で返す
  return gets.chomp.split(" ").map(&:to_i)
end
def get_f() #空白区切の入力を数値(実数)の配列で返す
  return gets.chomp.split(" ").map(&:to_f)
end
def get() #空白区切の入力を文字列の配列で返す
  return gets.chomp.split(" ")
end
def get_nsp() #入力されたものを一文字ずつに区切った文字列の配列で返す
  return gets.chomp.split("")
end 
def yn_judge(bool,y="Yes",n="No") #boolに真偽を投げることで、trueならy、falseならnの値を出力する
  return bool ? y : n 
end
def array(size,n=1,init=nil) #nに配列の次元数、sizeに配列の大きさ、initに初期値を投げることでその配列を返す
  if n==1
    return Array.new(size){init}
  else
    return Array.new(n).map{Array.new(size){init}}
  end
end

class Edge
  attr_accessor:to,:from,:c
  def initialize(from,to,cost)
    @to=to
    @from=from
    @c=cost
  end
end
class PriorityQueue
  attr_accessor:heap,:n
  def initialize(n)
    @n=0
    @heap=array(2,n,Float::INFINITY)
  end

  def push(x)
    i=@n
    @n+=1
    while i>0
      p=(i-1)/2
      break if @heap[p][0]<=x[0]
      @heap[i]=@heap[p]
      i=p
    end
    @heap[i]=x
  end

  def pop()
    res=@heap[0]
    @n-=1
    x=@heap[@n]
    i=0
    while i*2+1<=@n
      l=i*2+1
      r=i*2+2
      l=r if r<=@n and @heap[r][0]<=@heap[l][0]
      break if @heap[l][0]>=x[0]
      @heap[i]=@heap[l]
      i=l
    end
    @heap[i]=x
    return res
  end
end
V,E,r=get_i
edges=Array.new(V){[]}
E.times do|i|
  s,t,d=get_i
  edges[s].push(Edge.new(s,t,d))
end

pq=PriorityQueue.new(V)
d=array(V,1,Float::INFINITY)
d[r]=0
pq.push([d[r],r])

until pq.n.zero?
  v=pq.pop()
  next if v[0]==Float::INFINITY and v[1]==Float::INFINITY
  if d[v[1]]<v[0]
    next
  else
    next if edges[v[1]].nil?
    edges[v[1]].each do|e|
      if d[e.to]>d[v[1]]+e.c
        d[e.to]=d[v[1]]+e.c
        pq.push([d[e.to],e.to])
      end
    end
  end
end
d.each do|dis|
  if dis==Float::INFINITY
    puts "INF"
  else
    puts dis
  end
end
