前言
題意
有 \(n\) 個點,\(q\) 次連邊,以及起點 \(s\) 。連邊具體分三種:
- \(1\) \(v\) \(u\) \(w\) 從 \(v\) 到 \(u\) 連一條邊。
- \(2\) \(v\) \(l\) \(r\) \(w\) 從 \(v\) 向 \(l\) 到 \(r\) 所有點連一條邊。
- \(3\) \(v\) \(l\) \(r\) \(w\) 從 \(l\) 到 \(r\) 所有點向 \(v\) 連一條邊。
求所有點的最短路。
思路
以前做過一道比較類似的題:T'ill It's Over 都是線段樹優化連邊。
建立兩棵線段樹記為 \(A\) , \(B\) ,線段樹 \(A\) 的所有結點向上連結父節點,線段樹 \(B\) 的所有節點向下連結自己的子節點。
那麼操作就可以看成 \(A\) 樹對應區間連向 \(B\) 樹對應區間,就轉換為普通的線段樹區間查詢了。
現在需要考慮如何將 \(B\) 樹的狀態轉換到 \(A\) 樹上。
只需要將 \(B\) 樹的結點連向 \(A\) 的對應結點就好了。
由於狀態最後都會迴歸到 \(A\) 樹中,所以最後查詢 \(A\) 樹中葉子結點區間為 \([i,i](i\in [1,n])\) 的點的最短路徑,即為答案。
演算法總時間複雜度為 \(O(n\log n*log((n+q)\log n))\) ,大概就是 \(O(n\log^2n)\) 級別的。
Code
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
namespace Quick_Function {
template <typename Temp> void Read(Temp &x) {
x = 0; char ch = getchar(); bool op = 0;
while(ch < '0' || ch > '9') { if(ch == '-') op = 1; ch = getchar(); }
while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); }
if(op) x = -x;
}
template <typename T, typename... Args> void Read(T &t, Args &... args) { Read(t); Read(args...); }
template <typename Temp> Temp Max(Temp x, Temp y) { return x > y ? x : y; }
template <typename Temp> Temp Min(Temp x, Temp y) { return x < y ? x : y; }
template <typename Temp> Temp Abs(Temp x) { return x < 0 ? (-x) : x; }
template <typename Temp> void Swap(Temp &x, Temp &y) { x ^= y ^= x ^= y; }
}
using namespace Quick_Function;
#define INF 1e17
#define int long long
const int MAXN = 5e5 + 5;
const int MAXM = 5e6 + 5;
struct Edge { int To, Dist, Next; };
int head[MAXN], edgetot = 1;
Edge edge[MAXM];
void Addedge(int u, int v, int w) { edge[++edgetot].Next = head[u], edge[edgetot].To = v, edge[edgetot].Dist = w, head[u] = edgetot; }
struct Node {
int id, dis;
Node() {}
Node(int I, int D) { id = I, dis = D; }
friend bool operator < (Node x, Node y) { return x.dis > y.dis; }
};
priority_queue<Node> que;
int dis[MAXN];
bool vis[MAXN];
int n, q, s, p;
struct Segment_Node {
int l, r, id;
#define LS(x) (x << 1)
#define RS(x) (x << 1 | 1)
};
struct Segment_Tree {
Segment_Node t[MAXN << 2][2];//0為A線段樹,1為B線段樹
int tot;
void Build(int pos, int l, int r, int flag) {//初始化建樹
t[pos][flag].l = l, t[pos][flag].r = r, t[pos][flag].id = ++tot;
if(l == r) {
if(l == s && !flag) p = t[pos][flag].id;
if(flag) Addedge(t[pos][1].id, t[pos][0].id, 0);//這裡忘寫調了半個小時QAQ
return;
}
int mid = (l + r) >> 1;
Build(LS(pos), l, mid, flag); Build(RS(pos), mid + 1, r, flag);
if(!flag) {
Addedge(t[LS(pos)][flag].id, t[pos][flag].id, 0);
Addedge(t[RS(pos)][flag].id, t[pos][flag].id, 0);
}
else {
Addedge(t[pos][flag].id, t[LS(pos)][flag].id, 0);
Addedge(t[pos][flag].id, t[RS(pos)][flag].id, 0);
Addedge(t[pos][1].id, t[pos][0].id, 0);
}
}
int Query(int pos, int x, int flag) {//查詢區間為[x,x]的葉子結點
if(t[pos][flag].l == t[pos][flag].r) return t[pos][flag].id;
if(x <= t[LS(pos)][flag].r) return Query(LS(pos), x, flag);
else return Query(RS(pos), x, flag);
}
void Update1(int pos, int l, int r, int x, int w) {//一連多
if(l <= t[pos][1].l && t[pos][1].r <= r) {
Addedge(Query(1, x, 0), t[pos][1].id, w);
return;
}
if(l <= t[LS(pos)][1].r) Update1(LS(pos), l, r, x, w);
if(r >= t[RS(pos)][1].l) Update1(RS(pos), l, r, x, w);
}
void Update2(int pos, int l, int r, int x, int w) {//多連一
if(l <= t[pos][0].l && t[pos][0].r <= r) {
Addedge(t[pos][0].id, Query(1, x, 1), w);
return;
}
if(l <= t[LS(pos)][0].r) Update2(LS(pos), l, r, x, w);
if(r >= t[RS(pos)][0].l) Update2(RS(pos), l, r, x, w);
}
};
Segment_Tree tree;
void Dijkstra() {//最短路
for(int i = 1; i <= tree.tot; i++) dis[i] = INF;
que.push(Node(p, 0)); dis[p] = 0;
while(!que.empty()) {
int u = que.top().id; que.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i = head[u]; i; i = edge[i].Next) {
int v = edge[i].To;
if(dis[v] > dis[u] + edge[i].Dist) {
dis[v] = dis[u] + edge[i].Dist;
que.push(Node(v, dis[v]));
}
}
}
}
signed main() {
Read(n, q, s);
tree.Build(1, 1, n, 0); tree.Build(1, 1, n, 1);
for(int i = 1, opt, u, v, l, r, w; i <= q; i++) {
Read(opt, u);
if(opt == 1) {
Read(v, w);
tree.Update1(1, v, v, u, w);
}
else if(opt == 2) {
Read(l, r, w);
tree.Update1(1, l, r, u, w);
}
else {
Read(l, r, w);
tree.Update2(1, l, r, u, w);
}
}
Dijkstra();
for(int i = 1; i <= n; i++) printf("%lld ", dis[tree.Query(1, i, 0)] == INF? -1 : dis[tree.Query(1, i, 0)]);
return 0;
}