码迷,mamicode.com
首页 > 其他好文 > 详细

CF567E President and Roads

时间:2018-08-10 23:24:11      阅读:202      评论:0      收藏:0      [点我收藏+]

标签: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;
}

  

CF567E President and Roads

标签:using   inf   std   stat   优先   习惯   one   ges   长度   

原文地址:https://www.cnblogs.com/headboy2002/p/9457336.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!