//#define NDEBUG
#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 <iostream>
#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;

template <typename T, bool P = false> class PairingHeap {
	static constexpr std::uint_fast64_t ALLOCSIZE = (std::uint_fast64_t)1 << 10;

public:
	struct node_t {
		T data;
		node_t *left, *right, *per;
	};

private:
	node_t *root;
	std::uint_fast32_t size_;
	static node_t *merge(node_t *x, node_t *y) {
		if (P ^ (x->data < y->data))
			std::swap(x, y);
		if (x->right = y->left)
			y->left->per = x;
		y->left = x;
		x->per = y;
		return y;
	}

public:
	PairingHeap() : root(nullptr), size_(0) {}
	const T &top() const {
		assert(root);
		return root->data;
	}
	const T &pop() {
		assert(root);
		--size_;
		const T &ret = root->data;
		node_t *n = nullptr, *x = root->left, *y;
		while (x) {
			y = x;
			if (x->right) {
				x = x->right->right;
				y = merge(y, y->right);
			}
			else {
				x = nullptr;
			}
			y->right = n;
			n = y;
		}
		if (!n) {
			root = nullptr;
			return ret;
		}
		root = n;
		n = n->right;
		while (n) {
			x = n;
			n = n->right;
			root = merge(root, x);
		}
		root->per = nullptr;
		root->right = nullptr;
		return ret;
	}
	node_t *push(const T &data) {
		static node_t *pool = nullptr;
		static std::uint_fast64_t it = ALLOCSIZE;
		if (it == ALLOCSIZE) {
			pool = (node_t *)malloc(ALLOCSIZE * sizeof(node_t));
			it = 0;
		}
		node_t *top = &pool[it++];
		top->data = data;
		top->left = top->right = top->per = nullptr;
		root = root ? merge(root, top) : top;
		++size_;
		return top;
	}
	bool empty() { return !root; }
	std::uint_fast32_t size() { return size_; }
	void meld(PairingHeap<T, P> &other) {
		size_ += other.size_;
		other.size_ = 0;
		if (other.root) {
			root = root ? merge(root, other.root) : other.root;
			other.root = nullptr;
		}
	}
	void decrease(node_t *const x, const T &data) {
		assert(x);
		assert(!(P ^ (x->data < data)));
		x->data = data;
		if (!x->per)return;
		if (x->right)
			x->right->per = x->per;
		if (x->per->right != x)
			x->per->left = x->right;
		else
			x->per->right = x->right;
		x->per = nullptr;
		x->right = nullptr;
		root = merge(root, x);
	}
};
struct edge {
	uint32 to;
	uint64 cost;
};

int main(void) {
	uint32 n, m;
	uint64 t;
	std::cin >> n >> m >> t;
	std::vector<std::vector<edge>> G(n);
	for (uint32 i = 0;i < m;++i) {
		uint32 a, b, c;
		std::cin >> a >> b >> c;
		G[a].push_back({ b,c });
	}
	using P = std::pair<uint64, uint32>;
	PairingHeap<P> Q;
	static constexpr uint64 INF = std::numeric_limits<uint64>::max();
	std::vector<uint64> d(n, INF);
	std::vector<PairingHeap<P>::node_t *> keys(n,nullptr);
	Q.push({ 0,t });
	d[t] = 0;
	uint32 v;uint64 c;
	while (!Q.empty()) {
		std::tie(c, v) = Q.pop();
		if (d[v] < c) continue;
		for (const edge &e : G[v]) {
			if (c + e.cost < d[e.to]) {
				d[e.to] = c + e.cost;
				if (keys[e.to])Q.decrease(keys[e.to], { d[e.to],e.to });
				else keys[e.to] = Q.push({ d[e.to],e.to });
			}
		}
	}
	for (const uint64 D : d) {
		if (D == INF)puts("INF");
		else printf("%lu\n", D);
	}
	return 0;
}


Measured memory_bytes: 21740