洛谷 P2934 安全出行Safe Travel

rhdeng

2020-01-13 12:54:35

Solution

[传送~](https://www.luogu.org/problemnew/show/P2934) ## Description 有一张n个点m条边的无向图,对于每个u($1<u \leq n$),求删去1到u的最短路的最后一条边后,从1到u的最短距离 ## Solution 首先建出最短路树。新的最短路肯定不能走它到父亲的边,那么必须要找一个或两个中介点,使得它与1连通。设当前点位i,其中一个中介点位u。如果i与u有一条边直接连接,则距离为$dis_u+w_{u,i}$;或者在i的子树中存在一点v与u有边相连,则距离为$dis_u+w_{u,v}+dis_v-dis_i$。将两种情况统一,若u与点v有边相连(v=i或v在i的子树内),则新的距离为$dis_u+w_{u,v}+dis_v-dis_i$。由于$dis_i$确定,所以只要$dis_u+w_{u,v}+dis_v$最小即可。到这里与上面的题解都差不多,但是这里给出一种用堆的做法。 在dfs的过程中,把每个点可以得到的所有$dis_u+w_{u,v}+dis_v$存在一个堆里。具体做法是先把所有儿子的堆合并(要用左偏树),再枚举出边把新的u及其值加入堆中,最后取堆顶时删掉不满足条件的(即两个点都在子树内)。 ## Code ```cpp #include <bits/stdc++.h> #define mk make_pair #define fst first #define snd second using namespace std; const int maxn = 1e5; const int maxm = 2e5; const int maxt = 4e6; const int inf = 1e9; typedef pair<int, int> pii; int n, m, tot, cnt, head[maxn+10], dis[maxn+10], done[maxn+10], pd[maxm*2+10], dfn[maxn+10], size[maxn+10], ans[maxn+10], root[maxn+10]; struct edge { int to, nex, dis; } edges[maxm*2+10]; struct node { int to, ls, rs, fa, dis, val; } t[maxt+10]; void addedge(int u, int v, int w) { edges[++tot] = (edge){v, head[u], w}; head[u] = tot; edges[++tot] = (edge){u, head[v], w}; head[v] = tot; } int find(int x) { return x == t[x].fa ? x : t[x].fa = find(t[x].fa); } int merge(int x, int y) { if (!x || !y) return x+y; if (t[x].val > t[y].val) swap(x, y); t[x].rs = merge(t[x].rs, y); t[t[x].rs].fa = x; if (t[t[x].ls].dis < t[t[x].rs].dis) swap(t[x].ls, t[x].rs); t[x].dis = t[t[x].rs].dis+1; return x; } int remove(int x) { /*t[t[x].ls].fa = t[x].ls; t[t[x].rs].fa = t[x].rs; t[x].fa = merge(t[x].ls, t[x].rs); t[x].ls = t[x].rs = t[x].dis = 0; return t[x].fa;*/ return merge(t[x].ls, t[x].rs); } void dijkstra() { memset(dis, 0x3f, sizeof(dis)); priority_queue<pii, vector<pii>, greater<pii> > q; q.push(mk(dis[1] = 0, 1)); while (!q.empty()) { int x = q.top().snd; q.pop(); if (done[x]) continue; done[x] = 1; for (int i = head[x]; i; i = edges[i].nex) { edge e = edges[i]; if (dis[e.to] > dis[x]+e.dis) q.push(mk(dis[e.to] = dis[x]+e.dis, e.to)); } } } int check(int u, int v) { return dfn[v] >= dfn[u] && dfn[v] < dfn[u]+size[u]; } void dfs(int u, int fa) { dfn[u] = ++size[0]; size[u] = 1; for (int i = head[u]; i; i = edges[i].nex) { int v = edges[i].to; if (v == fa || !pd[i]) continue; dfs(v, u); root[u] = merge(root[u], root[v]); size[u] += size[v]; } for (int i = head[u]; i; i = edges[i].nex) { int v = edges[i].to; if (v == fa || pd[i]) continue; t[++cnt] = (node){v, 0, 0, cnt, 0, dis[u]+dis[v]+edges[i].dis}; root[u] = merge(root[u], cnt); } while (check(u, t[root[u]].to)) root[u] = remove(root[u]); ans[u] = root[u] ? t[root[u]].val-dis[u] : - 1; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= m; ++i) { int u, v, w; scanf("%d%d%d", &u, &v, &w); addedge(u, v, w); } dijkstra(); for (int i = 1; i <= n; ++i) for (int j = head[i]; j; j = edges[j].nex) if (dis[edges[j].to] == dis[i]+edges[j].dis) pd[j] = 1; cnt = n; dfs(1, 0); for (int i = 2; i <= n; ++i) printf("%d\n", ans[i]); return 0; } ``` > *THANK YOU FOR READING THIS!*