[题解] CF786B Legacy

前言

题目链接

题意

\(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;
}
posted @ 2021-05-15 13:54  Last_Breath  阅读(88)  评论(0编辑  收藏  举报