标签:using inf std stat 优先 习惯 one ges 长度
给出一个有向图,给出起点和终点,问每条边,是否图中存在的每一条起点到终点的最短路径都经过它(条件YES),如果存在不经过它的最短路径,可以减小它的多少边权(减少量不得超过该边原来的边权)使得该边满足条件YES。
我们规定图中起点到终点的所有最短路径所经过的点、边构成的子图叫做最短路径子图。判断一条边是否在最短路径子图中很容易,从起点来一遍Dijkstra得到每个节点到起点的最短距离v->DistS,从终点来一遍Dijkstra得到每个节点到终点的距离v->DistT。如果边e->From->DistS + e + e->To->DistT==最短路径长度,则边e在最短路径子图中。
问题就卡在一条在最短路径子图中的边是否满足条件YES。我曾经想通过类似于Bfs的方式,运用优先队列,key值边权,值是边的指针。但是这样做会出现各种各样的问题。所以以后记住,自己发明的Bfs、Dfs算法往往都不对,要是考试,不要在这个方面上抠!
我们发现,如果将最短路径子图变成无向图,一条在最短路径子图上的边在一个点双连通分量中与该边不满足YES是等价命题。所以当时我想:我们把最短路径子图上的点双连通分量全求出来,那么其它的边就都满足YES了。但是求点双连通分量细节多多,麻烦。所以我们要反着想:如果一条边满足条件YES,也就是这条边不在点双连通分量中,那么这条边是什么?割边嘛!所以在子图上Tarjan即可。
PS:感谢CodeForce,以前写Dijkstra时,习惯于在优先队列里维护节点指针,key值为节点指针对应的节点的Dist。然而这样是错的,因为一个节点在操作过程中,它Dist会被改掉,这样堆的key值就乱了。这个错误方法曾经在无数洛谷题中屡试不爽,这次让我发现了问题。
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <vector> using namespace std; #define Pair pair<long long, Node*> const int MAX_NODE = 200010, MAX_EDGE = 400010; const long long INF = 1e17; struct Node; struct Edge; struct Node { Edge *HeadS, *HeadT, *Head; long long Dist, DistT; bool Done; int Low, DfsN; }_nodes[MAX_NODE], *Start, *Target; int TotNode; struct Edge { Node *To; Edge *Next, *Rev; long long Weight; bool InSubG;//InSubGraph bool IsCut; }_edges[MAX_EDGE]; int TotEdge; void AddEdge(int uId, int vId, int eId, int w) { Node *from = _nodes + uId, *to = _nodes + vId; Edge *e = _edges + eId, *revE = _edges + eId + TotEdge; e->To = to; e->Weight = w; e->Next = from->HeadS; from->HeadS = e; revE->To = from; revE->Weight = w; revE->Next = to->HeadT; to->HeadT = revE; e->Rev = revE; revE->Rev = e; } struct HeapNode { long long Dist; Node *CurNode; HeapNode(long long dist, Node *curNode):Dist(dist), CurNode(curNode){} bool operator < (const HeapNode& a) const { return Dist > a.Dist; } }; void Dijkstra(Node *start) { for (int i = 1; i <= TotNode; i++) { _nodes[i].Dist = INF; _nodes[i].Done = false; } start->Dist = 0; static priority_queue<HeapNode> q; q.push(HeapNode(0, start)); while (!q.empty()) { HeapNode temp = q.top(); q.pop(); Node *cur = temp.CurNode; if (cur->Done) continue; cur->Done = true; for (Edge *e = cur->Head; e; e = e->Next) { if (cur->Dist + e->Weight < e->To->Dist) { e->To->Dist = cur->Dist + e->Weight; q.push(HeapNode(e->To->Dist, e->To)); } } } } void MinDist_T()//getMinDistFromTargetNode { for (int i = 1; i <= TotNode; i++) _nodes[i].Head = _nodes[i].HeadT; Dijkstra(Target); } void MinDist_S()//getMinDistFromStartNode { for (int i = 1; i <= TotNode; i++) { _nodes[i].DistT = _nodes[i].Dist; _nodes[i].Head = _nodes[i].HeadS; } Dijkstra(Start); } void GetSubGraph() { for (int i = 1; i <= TotEdge; i++) if (_edges[i].Rev->To->Dist + _edges[i].Weight + _edges[i].To->DistT == Target->Dist) _edges[i].InSubG = _edges[i].Rev->InSubG = true; } void CombineGraph() { for (int i = 1; i <= TotNode; i++) { _nodes[i].Head = _nodes[i].HeadS; Edge **e = &_nodes[i].Head; while (*e) e = &(*e)->Next; *e = _nodes[i].HeadT; } } int DfsCnt; void Dfs(Node *cur, Edge *from) { cur->Low = cur->DfsN = ++DfsCnt; for (Edge *e = cur->Head; e; e = e->Next) { if (!e->InSubG) continue; if (!e->To->DfsN) { Dfs(e->To, e); cur->Low = min(cur->Low, e->To->Low); if (cur->DfsN < e->To->Low) e->IsCut = e->Rev->IsCut = true; } else if (e->Rev != from) cur->Low = min(cur->Low, e->To->DfsN); } } void SolveEdge(Edge *e) { if (e->IsCut) { printf("YES\n"); return; } if (e->Rev->To->Dist == INF || e->To->DistT == INF) { printf("NO\n"); return; } long long delta = e->Rev->To->Dist - Target->Dist + e->Weight + e->To->DistT + 1; if (delta >= e->Weight) printf("NO\n"); else printf("CAN %lld\n", delta); } int main() { int s, t; scanf("%d%d%d%d", &TotNode, &TotEdge, &s, &t); Start = _nodes + s; Target = _nodes + t; for (int i = 1; i <= TotEdge; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); AddEdge(u, v, i, w); } MinDist_T(); MinDist_S(); GetSubGraph(); CombineGraph(); Dfs(Start, NULL); for (int i = 1; i <= TotEdge; i++) SolveEdge(_edges + i); return 0; }
标签:using inf std stat 优先 习惯 one ges 长度
原文地址:https://www.cnblogs.com/headboy2002/p/9457336.html