• 【CF786B】Legacy


    题目大意:初始给定 N 个点,支持三种操作:两点之间连边;一个点与一个连续区间编号的点之间连边;一个连续区间内的点和一个点连边,求执行 N 次操作之后的单源最短路。

    题解:学会了线段树优化建图。
    发现若暴力进行连边,时间和空间都会被卡到 (O(n^2)),直接起飞。
    发现连边的点的编号是连续的,结合线段树可以维护连续区间信息的思想,就产生了线段树优化建图的方法。
    在初始的 N 个节点的基础上建立两棵线段树,分别表示入树和出树,其中入树的上的各个节点允许单个节点的连接;出树上的节点允许连接单个节点。对于入树中的每个节点来说,需要连一条边权为 0 的有向边到它的两个儿子,表示若有节点连向父节点,那么一定也连向了儿子节点;对于出树上的每个节点来说,需要连一条边权为 0 的有向边到它的父节点,表示若当前节点可以连向某个节点,那么其子节点也一定可以走到这个节点。
    时空复杂度分析:空间为两棵线段树的空间减去一份叶子节点的大小,即:(3 * n),时间复杂度为 (elog^2v),其中 e 是边的条数,v 是节点的个数。

    代码如下

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    struct edge {
    	int to;
    	LL w;
    	edge(int x = -1, int y = -1) {
    		to = x, w = y;
    	}
    };
    
    struct segtree {
    	#define ls(o) tree[o].lc
    	#define rs(o) tree[o].rc
    	struct node {
    		int lc, rc;
    	};
    	vector<node> tree;
    	int tot, rootin, rootout;
    	segtree(int n, vector<vector<edge>> &adj) {
    		tot = n;
    		tree.resize(3 * n);
    		buildin(rootin, 1, n, adj);
    		buildout(rootout, 1, n, adj);
    	}
    	void buildin(int &o, int l, int r, vector<vector<edge>> &adj) {
    		if (l == r) {
    			o = l;
    			return;
    		}
    		o = ++tot;
    		int mid = l + r >> 1;
    		buildin(ls(o), l, mid, adj);
    		buildin(rs(o), mid + 1, r, adj);
    		adj[o].emplace_back(ls(o), 0);
    		adj[o].emplace_back(rs(o), 0);
    	}
    	void buildout(int &o, int l, int r, vector<vector<edge>> &adj) {
    		if (l == r) {
    			o = l;
    			return;
    		}
    		o = ++tot;
    		int mid = l + r >> 1;
    		buildout(ls(o), l, mid, adj);
    		buildout(rs(o), mid + 1, r, adj);
    		adj[ls(o)].emplace_back(o, 0);
    		adj[rs(o)].emplace_back(o, 0);
    	}
    	void link(int o, int l, int r, int x, int y, int u, int w, int opt, vector<vector<edge>> &adj) {// opt 2 -> in  3 -> out
    		if (l == x && r == y) {
    			opt == 2 ? adj[u].emplace_back(o, w) : adj[o].emplace_back(u, w);
    			return;
    		}
    		int mid = l + r >> 1;
    		if (y <= mid) {
    			link(ls(o), l, mid, x, y, u, w, opt, adj);
    		} else if (x > mid) {
    			link(rs(o), mid + 1, r, x, y, u, w, opt, adj);
    		} else {
    			link(ls(o), l, mid, x, mid, u, w, opt, adj);
    			link(rs(o), mid + 1, r, mid + 1, y, u, w, opt, adj);
    		}
    	}
    };
    
    int main() {
    	ios::sync_with_stdio(0);
    	cin.tie(0), cout.tie(0);
    	int n, m, s;
    	cin >> n >> m >> s;
    	vector<vector<edge>> adj(3 * n);
    	segtree t(n, adj);
    	while (m--) {
    		int opt;
    		cin >> opt;
    		if (opt == 1) {
    			int u, v, w;
    			cin >> u >> v >> w;
    			adj[u].emplace_back(v, w);
    		} else {
    			int u, l, r, w;
    			cin >> u >> l >> r >> w;
    			t.link(opt == 2 ? t.rootin : t.rootout, 1, n, l, r, u, w, opt, adj);
    		}
    	}
    	vector<LL> d(3 * n, 1e18);
    	vector<bool> expand(3 * n);
    	priority_queue<pair<LL, int>> q;
    	auto dijkstra = [&]() {
    		d[s] = 0, q.push(make_pair(0, s));
    		while (!q.empty()) {
    			int u = q.top().second;
    			q.pop();
    			if (expand[u] == 1) {
    				continue;
    			}
    			expand[u] = 1;
    			for (auto e : adj[u]) {
    				int v = e.to, w = e.w;
    				if (d[v] > d[u] + w) {
    					d[v] = d[u] + w;
    					q.push(make_pair(-d[v], v));
    				}
    			}
    		}
    	};
    	dijkstra();
    	for (int i = 1; i <= n; i++) {
    		if (d[i] == 1e18) {
    			cout << "-1" << " ";
    		} else {
    			cout << d[i] << " ";
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    二叉搜索树
    稀疏图(邻接链表),并查集,最短路径(Dijkstra,spfa),最小生成树(kruskal,prim)
    稠密图(邻接矩阵),并查集,最短路径(Dijkstra,spfa),最小生成树(kruskal,prim)
    图算法模版
    图算法(邻接矩阵)
    win764位安装DataFactory出现的问题
    使用SQL SERVER需要注意的一些细节
    索引维护存储过程(作业调用)
    收缩日志文件夹
    查看数据库资源被占情况(锁)
  • 原文地址:https://www.cnblogs.com/wzj-xhjbk/p/11528900.html
Copyright © 2020-2023  润新知