#include using namespace std; typedef long long LL; const LL INF = 0x3f3f3f3f3f3f3f3f; const int N = 100100 << 3, M = N << 2; typedef pair PII; //最短路径 LL d[N]; //最短距离数组 bool st[N]; //邻接表 int e[M], h[N], idx, w[M], ne[M]; void add(int a, int b, int c) { e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; } struct Node { int l, r; int ls, rs; //左儿子编号,右儿子编号 } tr[N]; int rtin, rtout, cnt; //出树根编号,入树根编号,编号计数器(从n+1开始,前面n个留给真实节点) // l:左边界, r:右边界, 返回新创建的节点编号 int build1(int l, int r) { if (l == r) { tr[l] = {l, r}; // tr[l]:叶子节点比较牛B,不用计数器生成节点号,而是真实的[1~n]。这样才能真正实现最底层的重复利用,防止重复建设 return l; } int u = ++cnt; //非叶子节点利用cnt获取节点号 tr[u] = {l, r}; //记录区间范围 int mid = (l + r) >> 1; tr[u].ls = build1(l, mid); //构建左子树 tr[u].rs = build1(mid + 1, r); //构建右子树 //父节点向左右儿子连权值为0的边 add(u, tr[u].ls, 0), add(u, tr[u].rs, 0); //返回新创建的节点编号 return u; } int build2(int l, int r) { if (l == r) { tr[l] = {l, r}; return l; } int u = ++cnt; tr[u] = {l, r}; int mid = (l + r) >> 1; tr[u].ls = build2(l, mid); tr[u].rs = build2(mid + 1, r); //左右儿子向父节点连权值为0的边 add(tr[u].ls, u, 0), add(tr[u].rs, u, 0); return u; } /** * @brief 点向区间连边 或 区间向点连边 * * @param u 线段树根节点u * @param x 需要连边的点 * @param l 区间左边界 * @param r 区间右边界 * @param w 权值 */ void add1(int u, int x, int l, int r, int w) { if (l <= tr[u].l && tr[u].r <= r) { add(x, u, w); return; } if (l <= tr[tr[u].ls].r) add1(tr[u].ls, x, l, r, w); if (r >= tr[tr[u].rs].l) add1(tr[u].rs, x, l, r, w); } void add2(int u, int x, int l, int r, int w) { if (l <= tr[u].l && tr[u].r <= r) { add(u, x, w); return; } if (l <= tr[tr[u].ls].r) add2(tr[u].ls, x, l, r, w); if (r >= tr[tr[u].rs].l) add2(tr[u].rs, x, l, r, w); } void dijkstra(int s) { memset(d, 0x3f, sizeof(d)); d[s] = 0; priority_queue, greater> q; q.push({0, s}); while (q.size()) { int u = q.top().second; q.pop(); if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (d[j] > d[u] + w[i]) { d[j] = d[u] + w[i]; q.push({d[j], j}); } } } } int main() { //加快读入 ios::sync_with_stdio(false), cin.tie(0); memset(h, -1, sizeof h); int n, q, s; cin >> n >> q >> s; cnt = n; //可用的合法编号从n+1开始 rtin = build1(1, n); //构建入树 rtout = build2(1, n); //构建出树 int op, x, y, l, r, w; for (int i = 1; i <= q; i++) { cin >> op; if (op == 1) { //点x到点y有一条边权w的边 cin >> x >> y >> w; add(x, y, w); } else if (op == 2) { //点x到区间[l,r]有一条边权w的边 cin >> x >> l >> r >> w; add1(rtin, x, l, r, w); } else if (op == 3) { //区间[l,r]到点x有一条边权w的边 cin >> x >> l >> r >> w; add2(rtout, x, l, r, w); } } //最短路径 dijkstra(s); //输出最短路 for (int i = 1; i <= n; i++) printf("%lld ", d[i] == INF ? -1 : d[i]); putchar('\n'); return 0; }