Codeforces 567E President and Roads

@(题解)[Codeforces|Dijkstra|最短路|最短路径数|好题|567E]

Codeforces 567E President and Roads

  • 题意:给出一个有向图,给出起点s,和终点t,总是从s沿着最短路径走到t,对于每一条边询问是否一定会经过这条边,如果是输出yes。如果不是,能否通过把减少这条边的权值(但不能减少到0)使这条边成为沿着最短路走一定会经过的边。如果能,输出需要减少的最少权值,不能输出no。
  • 思路:从s到t的最短路可能有多条,所以对于每一条边,分为两种情况,一种情况是这条边属于某一条最短路,另一种情况时这条边不属于任何一条最短路。
    • 现在先讨论不属于任何一条最短路的这种情况。很明显现在我们需要找出这条边能否通过修改权值,使最短路一定通过这条边。所以如果我们能够找出当前经过这条边从s到t的最短距离是多少,则可以求出最少需要减少的最少权值。考虑这条边为u->v,长度为c。如果我们能够求出s到u的最短距离和t到v的最短距离,那么经过这条边从s到t的最短距离就为dist(s, u) + c + dist(t, v)。如果已知dist(s, t)就可以计算出需要减少多少了,如果需要减少的权值大于等于c,那么就肯定不行了。而求dist(s, u)和dist(t, v)。可以通过以s为起点的dijkstra算法,和以t为起点的反向图用dijkstra算法求得。
    • 现在我们讨论如果这条边属于某一条最短路的情况:我们知道从s到t的最短路可能有很多条,现在我们需要判断的是当前边是否是所有的最短路一定会经过的那条边。这里可以通过计算出从s到t的最短路一共有多少条,再计算出经过边e从s到t的最短路一共有多少条,然后比较二者是否相等,如果相等则可以肯定所有的最短路一定会经过边e,如果后者比前者小(很明显后者不可能大于前者),则可以肯定不是所有的最短路都经过了边e。仍然定义边e为u->v,边长为c。定义ways(s, u)为s到u的最短路有多少条。我们只需计算ways(s, u) * ways(t, v) 是否等于 ways(s, t)就好了。这里ways(s, u)可以用dijkstra很方便的算出。
  • Tricks:1.这里从s到t的最短路距离很大,会爆int,需要用long long。2.最短路径的条数很多,以至于long long都无法存下,所以需要用一个很大的数来对其取模。3.dijkstra算最短路径的条数的时候,当dist[e.to] == dist[v] + e.cost的时候并不需要将e.to压入队列,因为当v从优先队列中取出的时候,就保证了v点此时到s点的距离一定是最小距离了,并且s到v的最短路径数也不可能在更新了(因为之后没有比v点距离s点更近的点了)。所以这里直接把ways(s, e.to) 加上 ways(s, v)就好了。
  • 代码:代码写的很渣,各位看官莫见笑。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <queue>
#include <cmath>
#include <algorithm>
#include <stack>
#include <functional>
#include <map>
#include <vector>
#include <set>

using namespace std;
const long long INF = 1000000000000000000LL;  //这里用的是long long INF设置为 ox3f3f3f3f的话会比较小,会wa掉。
const int MAX_V = 100000 + 5;
const int MAX_E = 100000 + 5;
const int MOD = 1000776999;
long long sways[MAX_V], tways[MAX_V];
long long dist_s[MAX_V], dist_t[MAX_V];

struct Edge
{
    int from, to, cost;
    Edge(int from = 0, int to = 0, int cost = 0):from(from), to(to), cost(cost){}
};

Edge edges[MAX_E];
vector<Edge> G1[MAX_V];
vector<Edge> G2[MAX_V];
typedef pair<long long, int> P; //first是最短距离, second是定点的编号
int n, m, s, t;

void dijkstra(vector<Edge> G[MAX_V], int s, long long d[], long long ways[])
{
    int V = n + 1;
    priority_queue<P, vector<P>, greater<P> > que;
    fill(d, d + V, INF);
    d[s] = 0;
    ways[s] = 1;
    que.push(P(0, s));

    while(!que.empty()) {
        P p = que.top(); que.pop();
        int v = p.second;
        if(p.first > d[v]) continue;
        for(int i = 0; i < G[v].size(); i++) {
            Edge e = G[v][i];
            if(d[e.to] == d[v] + e.cost) {
                ways[e.to] =(ways[e.to] + ways[v]) % MOD;
            }
            else if(d[e.to] > d[v] + e.cost) {
                d[e.to] = d[v] + e.cost;
                ways[e.to] = ways[v];
                que.push(P(d[e.to], e.to));
            }
        }
    }
}
int main()
{
    scanf("%d%d%d%d", &n, &m, &s, &t);
    for(int i = 0; i < m; i++) {
        int u, v, c;
        scanf("%d%d%d", &u, &v, &c);
        edges[i] = Edge(u, v, c);
        G1[u].push_back(edges[i]);
        G2[v].push_back(Edge(v, u, c));
    }

    dijkstra(G1, s, dist_s, sways);
    dijkstra(G2, t, dist_t, tways);

    for(int i = 0; i < m; i++) {
        Edge e = edges[i];
        int u = e.from, v = e.to;
        if(dist_s[u] + dist_t[v] + e.cost == dist_s[t]) {
            if((sways[u]*tways[v])%MOD == sways[t]) {
                printf("YES\n");
            } else if(e.cost > 1) {
                printf("CAN 1\n");
            } else {
                printf("NO\n");
            }
        } else {
            long long tmp = dist_s[u] + dist_t[v] + e.cost - dist_s[t] + 1;
            if(e.cost - tmp > 0) {
                printf("CAN %I64d\n", tmp);
            } else {
                printf("NO\n");
            }

        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值