1 条题解
-
0
自动搬运
来自洛谷,原作者为

ghj1222
阿绫最可爱啦搬运于
2025-08-24 21:40:26,当前版本为作者最后更新于2018-09-13 14:36:55,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
请管理员修正本题难度标签并清除更改题目前的AC记录
题目大意:给定一无向图,求删除一条边后1到n最短路的最大值,以及方案数。
做法:我们先从1为起点、从n为起点跑两边dij,获得每一个点到起点1、终点n的最短距离,其实距离和边权之间的关系相当于构建了由1为根的和由n为根的最短路树---所有最短路组成的树。
不难发现,要删除一条边,并使得最短路增大,一定要删除最短路上的边。所以我们找到从1到n的一条最短路链。找的方法就是在由n为根的最短路树上从1开始向N走。
接下来我们从这个最短路链上每一个点,在以1为根和以n为根的最短路树上进行bfs(仅向儿子bfs)。目的是找到对于每一个不在最短路链上的点x,找到1到x最短路与1到n最短路最后一个重合的点l[x],你也可以理解为在以1为根的最短路树上x与n的lca。同理还有以n为根的最短路上x和1的lca。
接下来我们枚举每一条不在最短路链上的边u->v,不难想到,从1到N经过边u->v的最短路一定是1->l[u]->u->v->r[v]->n的这种形式。那么在1->n的最短路上,l[u]->r[v]这一段区间内任意一条边的删除,从1到n的最短路有可能变为1->l[u]->u->v->r[v]->n。由于是最短路,所以就要更新所有这种形式的最小值。并且所有不在1->N最短路链上的边都能影响一个区间,所以这就变成了一个区间最小值问题,可以用线段树维护。用线段树维护1->N最短路上的边,线段树每一个元素代表删除这条边后最短路的长度。
最后我们扫一遍整个线段树,找最大值即可。注意如果所有的值相等(且等于最短路径),说明无论删除哪一条边都不会使得最短路径长度增加,所以删除图里任意一条边均可,tot=m。
代码如下(没有坑,可以直接复制提交)
#include <bits/stdc++.h> using namespace std; int n, m, tot, res[100010]; struct SegmentTree { int tree[400010]; void init(int x, int l, int r) { tree[x] = 0x3f3f3f3f; if (l == r) return; int mid = (l + r) >> 1; init((x << 1), l, mid); init((x << 1) | 1, mid + 1, r); } void update(int x, int L, int R, int cl, int cr, int key) { if (cr < L || cl > R) return; if (cl >= L && cr <= R) { tree[x] = min(tree[x], key); return; } int mid = (cl + cr) >> 1; update(x << 1, L, R, cl , mid, key); update((x << 1) | 1, L, R, mid + 1, cr, key); } void pushdown(int x, int l, int r) { if (l == r) { res[l] = tree[x]; return; } tree[x << 1] = min(tree[x << 1], tree[x]); tree[(x << 1) | 1] = min(tree[(x << 1) | 1], tree[x]); int mid = (l + r) >> 1; pushdown(x << 1, l, mid); pushdown((x << 1) | 1, mid + 1, r); } }segtree; struct edge { int v, w, ne; }a[400010]; bool in[400010]; int h[100010], dis1[100010], dis2[100010], link[100010], id[100010], l[100010], r[100010]; bool v[100010]; void add(int x, int y, int z) { static int tmp = 0; a[++tmp] = (edge){y, z, h[x]}; h[x] = tmp; } void dij(int *dis, int src) { for (int i = 1; i <= n; i++) { dis[i] = 0x3f3f3f3f; v[i] = false; } dis[src] = 0; priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > >q; q.push(make_pair(0, src)); while (!q.empty()) { int x = q.top().second; q.pop(); if (v[x]) continue; v[x] = true; for (int i = h[x]; i != 0; i = a[i].ne) { if (v[a[i].v] == false && dis[x] + a[i].w < dis[a[i].v]) { dis[a[i].v] = dis[x] + a[i].w; q.push(make_pair(dis[a[i].v], a[i].v)); } } } } void getLink() { int x = 1; while (x != n) { link[++tot] = x; id[x] = tot; for (int i = h[x]; i != 0; i = a[i].ne) { if (dis2[x] == dis2[a[i].v] + a[i].w) { in[i] = true; x = a[i].v; break; } } } link[++tot] = n; id[n] = tot; } void bfs(int src, int *dis, int *arr) { queue<int> q; q.push(link[src]); arr[link[src]] = src; while (!q.empty()) { int x = q.front(); q.pop(); for (int i = h[x]; i != 0; i = a[i].ne) if (id[a[i].v] == 0 && arr[a[i].v] == 0 && dis[x] + a[i].w == dis[a[i].v]) { arr[a[i].v] = src; q.push(a[i].v); } } } int main() { scanf("%d%d", &n, &m); for (int x, y, z, i = 1; i <= m; i++) { scanf("%d%d%d", &x, &y, &z); add(x, y, z); add(y, x, z); } dij(dis1, 1); dij(dis2, n); getLink(); for (int i = 1; i <= tot; i++) bfs(i, dis1, l); for (int i = tot; i >= 1; i--) bfs(i, dis2, r); segtree.init(1, 1, tot); for (int x = 1; x <= n; x++) for (int i = h[x]; i != 0; i = a[i].ne) if (in[i] == false && l[x] > 0 && r[a[i].v] > 0 && l[x] < r[a[i].v]) segtree.update(1, l[x], r[a[i].v] - 1, 1, tot, dis1[x] + a[i].w + dis2[a[i].v]); segtree.pushdown(1, 1, tot); int cnt = 0, ans = 0; for (int i = 1; i < tot; i++) { if (res[i] > ans) { ans = res[i]; cnt = 1; } else if (res[i] == ans) cnt++; } if (ans == dis1[n]) cnt = m; printf("%d %d\n", ans, cnt); return 0; }让我们一起膜拜大佬林瑞堂
https://www.luogu.org/space/show?uid=88460
- 1
信息
- ID
- 1728
- 时间
- 3000ms
- 内存
- 500MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者