codeforces786B Legacy 线段树优化建图
网址:https://codeforces.com/problemset/problem/786/B
题意:
给出$n$个城市和三种路径:$u$向$v$连一条带权无向边;$[l,r]$向$v$连一条带权无向边;$u$向$[l,r]$连一条带权无向边,给出一个起点$s$,求它到其他点的最短路径,如果不能到达,输出$-1$。
题解:
现在有三种操作:点对点连边,点对线段连边,线段对点连边。且点数多达$1e5$,所以就要建立线段树优化建图。建立入度树(就是边的终点)时,需要连上父节点往子节点的点且边权为$0$,建立出度树时,需要子节点往父节点连上边权为$0$的边。然后这里点对点,区间对点,点对区间连边,所以可以直接连边不需要超级结点,如果出现了区间对区间,就需要超级结点。然后连边之后在这个图上用一次$dijkstra$算法就行了。
AC代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN = 5e5 + 5; struct node { int l, r; }; node tr[MAXN]; struct edge { int to; ll w; edge() {} edge(int _to, ll _w) :to(_to), w(_w) {} bool operator<(const edge& a)const { return w > a.w; } }; vector<edge>G[MAXN]; int cnt = 0; void buildout(int& rt, int l, int r) { if (l == r) { rt = l; return; } rt = ++cnt; int m = (l + r) >> 1; buildout(tr[rt].l, l, m); buildout(tr[rt].r, m + 1, r); G[rt].push_back(edge(tr[rt].l, 0)); G[rt].push_back(edge(tr[rt].r, 0)); } void buildin(int& rt, int l, int r) { if (l == r) { rt = l; return; } rt = ++cnt; int m = (l + r) >> 1; buildin(tr[rt].l, l, m); buildin(tr[rt].r, m + 1, r); G[tr[rt].l].push_back(edge(rt, 0)); G[tr[rt].r].push_back(edge(rt, 0)); } void update(int rt, int l, int r, int ql, int qr, int v, int w, int type) { if (l <= ql && r >= qr) { type == 2 ? G[v].push_back(edge(rt, w)) : G[rt].push_back(edge(v, w)); return; } int m = (ql + qr) >> 1; if (l <= m) update(tr[rt].l, l, r, ql, m, v, w, type); if (r > m) update(tr[rt].r, l, r, m + 1, qr, v, w, type); } const ll INF = 0x3f3f3f3f3f3f3f3f; ll dis[MAXN]; bool vis[MAXN]; priority_queue<edge>Q; void dijkstra(int s) { memset(vis, 0, sizeof(vis)); memset(dis, 0x3f, sizeof(dis)); dis[s] = 0; Q.push(edge(s, dis[s])); while (Q.size()) { auto u = Q.top(); Q.pop(); if (vis[u.to]) continue; vis[u.to] = 1; for (auto i : G[u.to]) { int v = i.to; if (!vis[v] && dis[v] > dis[u.to] + i.w) { dis[v] = dis[u.to] + i.w; Q.push(edge(v, dis[v])); } } } } int rt1, rt2; int main() { int n, m, s; scanf("%d%d%d", &n, &m, &s); cnt = n + 1; buildin(rt2, 1, n);//区间出去 buildout(rt1, 1, n);//区间进来 int op, u, v, l, r, w; while (m--) { scanf("%d", &op); if (op == 1) { scanf("%d%d%d", &u, &v, &w); G[u].push_back(edge(v, w)); } else { scanf("%d%d%d%d", &v, &l, &r, &w); update(op == 2 ? rt1 : rt2, l, r, 1, n, v, w, op); } } dijkstra(s); for (int i = 1; i <= n; ++i) printf("%lld%c", dis[i] >= INF ? -1 : dis[i], i == n ? '\n' : ' '); return 0; }