//#define NDEBUG
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <utility>
#include <vector>

class RadixHeap32 {
	using uint32 = std::uint_fast32_t;
	using P = std::pair<uint32, uint32>;
	uint32 bsr(const uint32 x) {
		if (!x)
			return 0;
#ifdef __GNUC__
		return 32 - __builtin_clz(x);
#else
		uint32 t = 16;
		for (uint32 i = 3; ~i; --i) {
			if (x & ~(((uint32)1 << t) - 1)) {
				t += (uint32)1 << i;
			}
			else {
				t -= (uint32)1 << i;
			}
		}
		if (x & ~(((uint32)1 << t) - 1))
			++t;
		return t;
#endif
	}
	std::vector<P> v[33];
	uint32 last, size;

public:
	RadixHeap32() : last(0), size(0) {}
	P pop() {
		assert(size);
		--size;
		if (v[0].empty()) {
			uint32 i = 1;
			while (v[i].empty())
				++i;
			last = v[i][0].first;
			for (uint32 j = 1; j < v[i].size(); ++j)
				last = std::min(last, v[i][j].first);
			for (uint32 j = 0; j < v[i].size(); ++j)
				v[bsr(last ^ v[i][j].first)].push_back(v[i][j]);
			v[i].erase(v[i].begin(), v[i].end());
		}
		P ret = v[0].back();
		v[0].pop_back();
		return ret;
	}
	void push(const P &x) {
		assert(last <= x.first);
		++size;
		v[bsr(x.first ^ last)].push_back(x);
	}
	bool empty() { return !size; }
};
#define _CRT_SECURE_NO_WARNINGS
#include <algorithm>
#include <array>
#include <cassert>
#include <climits>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <functional>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
static constexpr double PI = 3.1415926535897932;

using int32 = std::int_fast32_t;
using int64 = std::int_fast64_t;
using uint32 = std::uint_fast32_t;
using uint64 = std::uint_fast64_t;
using intl32 = std::int_least32_t;
using intl64 = std::int_least64_t;
using uintl32 = std::uint_least32_t;
using uintl64 = std::uint_least64_t;

namespace n91 {
	template <typename T>
	auto scan(T &d) ->
		typename std::enable_if<std::is_same<T, std::string>::value>::type {
		d = std::string();
		int c = 0;
		while (c < 'a' || 'z' < c)
			c = fgetc(stdin);
		while ('a' <= c && c <= 'z') {
			d.push_back(c);
			c = fgetc(stdin);
		}
	}
	template <typename T>
	auto scan(T &d) ->
		typename std::enable_if<std::is_same<T, double>::value>::type {
		scanf("%lf", &d);
	}
	template <typename T>
	auto scan(T &d) ->
		typename std::enable_if<std::is_signed<T>::value ==
		std::is_same<T, std::string>::value>::type {
		d = 0;
		int c = 0;
		while (c < '0' || '9' < c)
			c = fgetc(stdin);
		while ('0' <= c && c <= '9') {
			d = d * 10 + c - '0';
			c = fgetc(stdin);
		}
	}
	template <typename T>
	auto scan(T &d) -> typename std::enable_if<std::is_signed<T>::value!= std::is_same<T, double>::value>::type {
		d = 0;
		int c = 0;
		bool f = 0;
		while (c < '0' || '9' < c) {
			if (c == '-')
				f = 1;
			c = fgetc(stdin);
		}
		while ('0' <= c && c <= '9') {
			d = d * 10 + c - '0';
			c = fgetc(stdin);
		}
		if (f)
			d = -d;
	}
} // namespace n91
int main(void) {
	constexpr uint32 INF = UINT32_MAX;
	using P = std::pair<uint32, uint32>;
	uint32 V, E, s;
	n91::scan(V);n91::scan(E);n91::scan(s);
	std::vector<std::vector<P>> G(V);
	uint32 a, b, d;
	while (E--) {
		n91::scan(a);
		n91::scan(b);
		n91::scan(d);
		G[a].emplace_back(d, b);
	}
	std::vector<uint32> dist(V, INF);
	std::vector<char> r(V, 0);
	RadixHeap32 Q;
	Q.push({ 0,s });
	while (!Q.empty()) {
		uint32 to;
		std::tie(d, to) = Q.pop();
		if (r[to]) continue;
		r[to] = 1;dist[to] = d;
		for (P e: G[to]) {
			if (dist[e.second] > d + e.first)
				Q.push({ d + e.first,e.second });
		}
	}
	for (uint32 i = 0;i < V;++i) {
		if (dist[i] == INF) {
			puts("INF");
		}
		else {
			printf("%u\n", dist[i]);
		}
	}
	return 0;
}



Measured memory_bytes: 22224