#include <array>
#include <queue>
#include <limits>
#include <cstdio>
#include <bitset>
#include <vector>
#include <utility>
#include <algorithm>
constexpr auto __MAX__ { static_cast<int>(1e6) };
constexpr int FORWARD { 0 };
constexpr int REVERSE { 1 };
typedef struct
__Edge__{
int refer;
int next;
int weigth;
} Edge;
template <typename __FROM__, typename __TO__>
struct Arc {
__FROM__ from;
__TO__ to;
int weight;
};
typedef struct __Path__ {
int distance {};
int numb {};
constexpr bool operator<(const __Path__ & other) noexcept
{ return distance < other.distance; }
} Path;
typedef struct __Range__ {
int first;
int last;
constexpr bool single() noexcept
{ return first + 1 == last; }
constexpr int middle() noexcept
{ return (first + last) / 2; }
constexpr __Range__ left_partitial() noexcept
{ return { first, middle() }; }
constexpr __Range__ right_partitial() noexcept
{ return { middle(), last }; }
constexpr bool contain(const __Range__ & other) noexcept
{ return first <= other.first && other.last <= last; }
constexpr int length() noexcept
{ return last - first; }
} Range;
int N {};
int Q {};
int S {};
int serial {};
std::vector<Edge> edges {};
std::array<int, __MAX__ << 2 | 1> head {};
std::array<int, __MAX__ << 2 | 1> numb[2u] {};
std::bitset<__MAX__> visited {};
std::array<int, __MAX__> distance {};
constexpr auto left(int index) noexcept { return index << 1; }
constexpr auto right(int index) noexcept { return index << 1 | 1; }
void connect(Arc<int, int> arc, int direction) noexcept {
auto [from, to, weight] = arc;
if(direction == REVERSE) std::swap(from, to);
auto temporary { static_cast<int>(edges.size()) };
edges.emplace_back(to, head[from], weight);
head[from] = temporary;
}
void build(Range shrink, int index, int direction) noexcept {
if(shrink.single())
return (void) (numb[direction][index] = shrink.first);
numb[direction][index] = ++ serial;
build(shrink.left_partitial(), left(index), direction);
build(shrink.right_partitial(), right(index), direction);
connect((Arc<int, int>){numb[direction][index], numb[direction][left(index)], 0}, direction);
connect((Arc<int, int>){numb[direction][index], numb[direction][right(index)], 0}, direction);
}
void update(Arc<int, Range> arc, Range shrink, int index, int direction) noexcept {
auto [integer, range, weight] = arc;
if(range.contain(shrink))
return (void) connect((Arc<int, int>){ integer, numb[direction][index], weight }, direction);
if(range.first < shrink.middle()) update(arc, shrink.left_partitial(), left(index), direction);
if(range.last >= shrink.middle()) update(arc, shrink.right_partitial(), right(index), direction);
}
void operation(int op) {
if(op == 1) {
int from {};
int to {};
int weight {};
std::scanf("%d%d%d", &from, &to, &weight);
connect((Arc<int, int>) {from, to, weight}, FORWARD);
return;
}
int point {},
weight {};
Range range {};
std::scanf("%d%d%d%d", &point, &range.first, &range.last, &weight);
if(op == 2)
update((Arc<int, Range>) {point, range, weight}, (Range) {1, N}, 1, FORWARD);
else
update((Arc<int, Range>) {point, range, weight}, (Range) {1, N}, 1, REVERSE);
}
void dijstra(int source) {
std::priority_queue<Path> heap {};
distance[source] = 0;
heap.emplace(0, source);
while(! heap.empty()) {
auto [current_numb, current_distance] = heap.top();
heap.pop();
if(visited[current_numb]) continue;
visited[current_numb] = 1;
for(int i {head[current_numb]}; ~i; i = edges[i].next) {
auto [refer, dummy_next, weight] = edges[i];
if(distance[refer] > distance[current_numb] + weight) {
distance[refer] = distance[current_numb] + weight;
heap.emplace(distance[refer], refer);
}
}
}
}
int main() {
head.fill(-1);
distance.fill(std::numeric_limits<int>::max());
std::scanf("%d", &N);
(void) std::exchange(serial, N);
build((Range){1, N}, 1, FORWARD);
build((Range){1, N}, 1, REVERSE);
std::scanf("%d", &Q);
while(Q --) {
int op {};
std::scanf("%d", &op);
operation(op);
}
std::scanf("%d", &S);
dijstra(S);
for(int i {1}; i <= N; ++i)
std::printf("%d", distance[i] == std::numeric_limits<int>::max() ? -1 : distance[i]);
return 0;
}