Codeforces E. President and Roads (优先队列Dijkstar + 强连通 找必最短路上的必须边(桥))

本文介绍了一个关于总统出行路径的问题,通过构建有向图并利用Dijkstra算法求解最短路径,进一步探讨如何优化路径上的道路以确保总统能够更快地到达目的地。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

E. President and Roads
time limit per test
 2 seconds
memory limit per test
 256 megabytes
input
 standard input
output
 standard output

Berland has n cities, the capital is located in city s, and the historic home town of the President is in city t (s ≠ t). The cities are connected by one-way roads, the travel time for each of the road is a positive integer.

Once a year the President visited his historic home town t, for which his motorcade passes along some path from s to t (he always returns on a personal plane). Since the president is a very busy man, he always chooses the path from s to t, along which he will travel the fastest.

The ministry of Roads and Railways wants to learn for each of the road: whether the President will definitely pass through it during his travels, and if not, whether it is possible to repair it so that it would definitely be included in the shortest path from the capital to the historic home town of the President. Obviously, the road can not be repaired so that the travel time on it was less than one. The ministry of Berland, like any other, is interested in maintaining the budget, so it wants to know the minimum cost of repairing the road. Also, it is very fond of accuracy, so it repairs the roads so that the travel time on them is always a positive integer.

Input

The first lines contain four integers nms and t (2 ≤ n ≤ 105; 1 ≤ m ≤ 105; 1 ≤ s, t ≤ n) — the number of cities and roads in Berland, the numbers of the capital and of the Presidents' home town (s ≠ t).

Next m lines contain the roads. Each road is given as a group of three integers ai, bi, li (1 ≤ ai, bi ≤ nai ≠ bi; 1 ≤ li ≤ 106) — the cities that are connected by the i-th road and the time needed to ride along it. The road is directed from city ai to city bi.

The cities are numbered from 1 to n. Each pair of cities can have multiple roads between them. It is guaranteed that there is a path froms to t along the roads.

Output

Print m lines. The i-th line should contain information about the i-th road (the roads are numbered in the order of appearance in the input).

If the president will definitely ride along it during his travels, the line must contain a single word "YES" (without the quotes).

Otherwise, if the i-th road can be repaired so that the travel time on it remains positive and then president will definitely ride along it, print space-separated word "CAN" (without the quotes), and the minimum cost of repairing.

If we can't make the road be such that president will definitely ride along it, print "NO" (without the quotes).

Sample test(s)
input
6 7 1 6
1 2 2
1 3 10
2 3 7
2 4 8
3 5 3
4 5 2
5 6 1
output
YES
CAN 2
CAN 1
CAN 1
CAN 1
CAN 1
YES
input
3 3 1 3
1 2 10
2 3 10
1 3 100
output
YES
YES
CAN 81
input
2 2 1 2
1 2 1
1 2 2
output
YES
NO
Note

The cost of repairing the road is the difference between the time needed to ride along it before and after the repairing.

In the first sample president initially may choose one of the two following ways for a ride: 1 → 2 → 4 → 5 → 6 or1 → 2 → 3 → 5 → 6.

题意 : 给 n<=10^5 个点 m <=10^5  条带权(即时间 1<=t<=10^6)边的有向图 (有重边), 现在要从起点VS 到终点 VT ,走的是最短时间。输出 m 行,如果第 i 行输出的是YES 表示 第 i  条边 是最短路的必经路。如果输出的是 CAN x  ,则表示 修改 第 i  条边(第 i 条边的边权 减少了 x )使得原来的最短路减少1,必经过这条边。如果输出 NO 表示无法修改减少边权 使得最短路减少 1 。修改边的原则是:修改后的边权>0。

解题: 先用优先队列优化Dijkstar(用spfa超时),求起点vs到每个点的最短时间 time1[],再求从终点vt 反向图到每个点的最短时间 time2[] 。现在要判断必经路:重新建一个无向图,图中的边都是属于求出的最短路上的有边。用 强连通 判断桥(即必经路也是必须路),因为断了桥图就变成了两个连通块,所以桥即必经路。


#include<stdio.h>
#include<string.h>
#include<queue>
#include<stack>
using namespace std;
#define ll __int64
const int N = 100005;
const ll INF = (ll)999999999999;
struct nnn
{
    int u,v;
    ll t;
}node[N];

struct EDG{
    int to,next;
    ll t;
}edg[N*3];

struct NNNN{
    int u;
    ll t;
    friend bool operator<(NNNN aa , NNNN bb){
        return aa.t>bb.t;
    }
};

int eid,head[N] , vs , vt , n ;
ll time1[N],time2[N]; //从 vs 到每个点的最短时间 和 从 vt 反向到每个点的最短时间
bool inq[N];
void init()
{
    eid=0;
    memset(head,-1,sizeof(head));
}
void addEdg(int u ,int v,ll t,ll tt=0)
{
    edg[eid].to=v; edg[eid].next=head[u];
    edg[eid].t=t; head[u]=eid++;

    edg[eid].to=u; edg[eid].next=head[v];
    edg[eid].t=tt; head[v]=eid++;
}
void dijstar(int flag)
{
    priority_queue<NNNN>q;
    NNNN now , pre;
    int u,v;
    for(int i=1; i<=n; i++){
        if(flag==0) time1[i]=INF;  else   time2[i]=INF;
        inq[i]=0;
    }
    now.t = 0;
    if(flag==0)
        time1[vs]=0 , now.u = vs;
    else
        time2[vt]=0 , now.u = vt;
    q.push(now);
     while(!q.empty())
     {
         pre=q.top(); q.pop();
         u=pre.u;
         if(inq[u]) continue;
         inq[u]=1;
         for(int i=head[u]; i!=-1; i=edg[i].next){
            v=edg[i].to;
            if(inq[v])continue;
            if(flag==0){
                if(time1[v]>time1[u]+edg[i].t&&edg[i].t!=INF){
                    time1[v]=time1[u]+edg[i].t;
                    now.t = time1[v] ;
                    now.u = v;
                    q.push(now);
                }
            }
            else{ //反向求最短时间
                int ti=i^1;
                if(time2[v]>time2[u]+edg[ti].t&&edg[i].t==INF){
                    time2[v]=time2[u]+edg[ti].t;
                    now.t = time2[v] ;
                    now.u = v;
                    q.push(now);
                }
            }
         }
     }
}

bool ok[N] , vistedg[N];
int low[N] , dfn[N] , deep ;
void tarjar(int u )
{
    low[u] = dfn[u] = ++deep;
    for(int i=head[u] ; i!=-1; i=edg[i].next){
        int v=edg[i].to ;
        if(vistedg[edg[i].t]){ //有重边找必经路,只能用标记边的办法才会正确,不用 v==f 
            head[u] = edg[i].next; continue;
        }
        head[u] = edg[i].next; //每条边只用一次
        vistedg[edg[i].t] = 1;
        if( !dfn[v] ){
            tarjar( v ) ;
            low[u] = low[u] < low[v] ? low[u] : low[v] ;

            if(dfn[u] < low[v]) ok[edg[i].t]=1;  //桥,则是必经路
        }
        else if(low[u]>dfn[v])
            low[u] = dfn[v] ;
    }
}
int main()
{
    int m , u , v ;
    ll t;
    while(scanf("%d%d%d%d",&n,&m,&vs,&vt)>0){
        init();
        for(int i=0; i<m; i++){
            scanf("%d%d%I64d",&u,&v,&t);
            node[i].u=u;
            node[i].v=v;
            node[i].t=t;
            addEdg(u , v , t , INF);
        }

        dijstar(0);
        dijstar(1);
        ll shorttime=time1[vt];
        deep=0;
        init();
        memset(ok , 0 , sizeof( ok ));
        memset(dfn , 0 , sizeof( dfn ));
        memset(vistedg , 0 , sizeof( vistedg ));
        for(int i=0; i<m; i++){ //重新建一个只有在最短路上的边的无向图,只用于找必须边,也就是找 桥 
            u=node[i].u;
            v=node[i].v;
            if(time1[u]+node[i].t+time2[v]==shorttime)
                addEdg(u,v,i,i) ;
        }
        tarjar( vs ) ;

        for(int i=0; i<m; i++)
        if(ok[i])
            printf("YES\n");
        else {
            u=node[i].u;
            v=node[i].v;
            t=shorttime-time1[u]-time2[v]-1; 
            if(t>0)
                printf("CAN %I64d\n",node[i].t-t);
            else
                printf("NO\n");
        }
    }
}


### Codeforces Div.2 比赛难度介绍 Codeforces Div.2 比赛主要面向的是具有基础编程技能到中级水平的选手。这类比赛通常吸引了大量来自全球不同背景的参赛者,包括大学生、高中生以及一些专业人士。 #### 参加资格 为了参加 Div.2 比赛,选手的评级应不超过 2099 分[^1]。这意味着该级别的竞赛适合那些已经掌握了一定算法知识并能熟练运用至少一种编程语言的人群参与挑战。 #### 题目设置 每场 Div.2 比赛一般会提供五至七道题目,在某些特殊情况下可能会更多或更少。这些题目按照预计解决难度递增排列: - **简单题(A, B 类型)**: 主要测试基本的数据结构操作和常见算法的应用能力;例如数组处理、字符串匹配等。 - **中等偏难题(C, D 类型)**: 开始涉及较为复杂的逻辑推理能力和特定领域内的高级技巧;比如图论中的短路径计算或是动态规划入门应用实例。 - **高难度题(E及以上类型)**: 对于这些问题,则更加侧重考察深入理解复杂概念的能力,并能够灵活组合多种方法来解决问题;这往往需要较的创造力与丰富的实践经验支持。 对于新手来说,建议先专注于理解和练习前几类较容易的问题,随着经验积累和技术提升再逐步尝试更高层次的任务。 ```cpp // 示例代码展示如何判断一个数是否为偶数 #include <iostream> using namespace std; bool is_even(int num){ return num % 2 == 0; } int main(){ int number = 4; // 测试数据 if(is_even(number)){ cout << "The given number is even."; }else{ cout << "The given number is odd."; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值