CF786B Legacy

Last_Breath發表於2021-05-15

前言

題目連結

題意

\(n\) 個點,\(q\) 次連邊,以及起點 \(s\) 。連邊具體分三種:

  1. \(1\) \(v\) \(u\) \(w\)\(v\)\(u\) 連一條邊。
  2. \(2\) \(v\) \(l\) \(r\) \(w\)\(v\)\(l\)\(r\) 所有點連一條邊。
  3. \(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;
}