zoukankan      html  css  js  c++  java
  • CF567E President and Roads

    题目大意

      给出一个有向图,给出起点和终点,问每条边,是否图中存在的每一条起点到终点的最短路径都经过它(条件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
    ");
            return;
        }
        if (e->Rev->To->Dist == INF || e->To->DistT == INF)
        {
            printf("NO
    ");
            return;
        }
        long long delta = e->Rev->To->Dist - Target->Dist + e->Weight + e->To->DistT + 1;
        if (delta >= e->Weight)
            printf("NO
    ");
        else
            printf("CAN %lld
    ", 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;
    }
    

      

  • 相关阅读:
    mysql采坑笔记
    git常用操作
    vscode配置及插件
    atom之插件安装及相关
    xshell中操作服务器笔记
    js学习笔记之自调用函数、闭包、原型链
    dragover event 翻译
    拖放事件笔记
    关于clear:both;后有固定高度的原因及解决方法
    weex打包android apk采坑之旅(windows)
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9457336.html
Copyright © 2011-2022 走看看