#include <bits/stdc++.h>
using namespace std;
int n, m;
int dis[10010]; // 存储起点到各点的最短距离
int u[10010], v[10010], w[10010]; // 边的起点、终点、权重
int first[10010], nxt[10010]; // 邻接表:first[u]是u的第一条边索引,nxt[i]是边i的下一条边索引
struct edge {
int to; // 目标节点
int w; // 距离(用于优先队列排序)
};
int vis[10010]; // 标记节点是否已确定最短路径
// 优先队列重载小于号,实现小根堆(按距离从小到大)
bool operator<(edge a, edge b) {
return a.w > b.w;
}
void dijkstra(int start) {
dis[start] = 0;
priority_queue<edge> pq;
pq.push({start, 0}); // 起点入队
while (!pq.empty()) {
edge U = pq.top(); // 取出当前距离最小的节点
pq.pop();
int u_node = U.to; // 当前处理的节点
if (vis[u_node]) continue; // 已确定最短路径,跳过
vis[u_node] = 1; // 标记为已确定
// 遍历当前节点的所有邻接边(邻接表遍历)
int k = first[u_node]; // 第一条边的索引
while (k != -1) { // 遍历所有边,直到没有下一条边
int v_node = v[k]; // 边的终点
// 松弛操作:如果通过当前节点到v_node的路径更短
if (dis[v_node] > dis[u_node] + w[k]) {
dis[v_node] = dis[u_node] + w[k]; // 更新距离
pq.push({v_node, dis[v_node]}); // 新距离入队
}
k = nxt[k]; // 下一条边
}
}
}
int main() {
cin >> n >> m;
// 初始化邻接表:所有节点的第一条边初始化为-1(无)
memset(first, -1, sizeof(first));
// 读入m条边,构建邻接表
for (int i = 1; i <= m; ++i) {
cin >> u[i] >> v[i] >> w[i];
nxt[i] = first[u[i]]; // 新边的下一条是原第一条边
first[u[i]] = i; // 更新第一条边为当前边
}
// 初始化距离数组:起点外的节点距离设为无穷大
memset(dis, 0x3f, sizeof(dis));
int start;
cin >> start;
dijkstra(start);
// 输出起点到各节点的最短距离
for (int i = 1; i <= n; ++i) {
// 若距离仍为无穷大,说明不可达,可输出特定符号(这里保持原值)
cout << dis[i] << ' ';
}
return 0;
}