import std.functional, std.algorithm, std.container, std.typetuple,
  std.typecons, std.bigint, std.string, std.traits, std.array, std.range, std.stdio, std.conv;

enum INF = (1 << 30);
alias Edge = Tuple!(long, "dst", long, "cost");

long[] dijkstra(in Edge[][] tree, long start) {
  alias PriorityQueue = BinaryHeap;
  alias tie = TypeTuple;

  size_t V = tree.length;
  auto dist = new long[V];
  fill(dist, INF);

  auto queue = new PriorityQueue!(Array!Edge, "a.cost >= b.cost")();

  dist[start] = 0;
  queue.insert(Edge(start, 0));
  while (!queue.empty()) {
    long v;
    long cost;
    tie!(v, cost) = queue.front();
    queue.popFront();

    if (dist[v] < cost) {
      continue;
    }
    foreach (const ref Edge e; tree[v]) {
      if (dist[v] + e.cost < dist[e.dst]) {
        dist[e.dst] = dist[v] + e.cost;
        queue.insert(Edge(e.dst, dist[e.dst]));
      }
    }
  }

  return dist;
}

enum M = INF;

string disp(long[] arr) {
  return arr.map!(x => x == M ? "M" : x.to!string).join(" ");
}

void main() {
  int[] VEr = readln.chomp.split.to!(int[]);
  int V = VEr[0], E = VEr[1], r = VEr[2];
 
  auto tree = new Edge[][](V, 0);
  foreach (_; 0 .. E) {
    int[] _std = readln.chomp.split.to!(int[]);
    int s = _std[0], t = _std[1], d = _std[2];
    tree[s] ~= Edge(t, d);
  }

  long[] ret = dijkstra(tree, r);

  foreach (i; 0 .. V) {
    long d = ret[i];
    if (d >= INF) {
      writeln("INF");
    } else {
      writeln(d);
    }
  }
}

