|
|
#include <bits/stdc++.h>
|
|
|
using namespace std;
|
|
|
typedef long long LL;
|
|
|
const LL INF = 0x3f3f3f3f3f3f3f3f;
|
|
|
const int N = 100100 << 3, M = N << 2;
|
|
|
typedef pair<LL, int> 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<PII, vector<PII>, greater<PII>> 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;
|
|
|
} |