题目描述

“题目名称只是吸引你来做题的啦,其实和题目没什么卵关系:o( ̄▽ ̄)o” —— 历史——殿堂
wpy移情别恋啦,他不喜欢spfa了,现在他喜欢使用dij,但是他又发现了一个新的问题,dij无法跑有负权边的图,于是wpy找到了她的男朋友也就是你来帮忙,为了你晚上的幸福生活,你必须在1秒内帮她解决这个问题,然后蹿到床上。。。balabala(捂脸)。。。。(/ω\)
简单来说,有一张n个点,m条边的有向图,请你给每条边确定一个新的边权(不同边之间可以不同),保证对于任意u,v,在新图上的u到v的最短路上的点和原图上最短路上的点相同且顺序不变。
新的边权要求非负。

输入描述:

第一行两个整数n,m,表示有n个点,m条边。
接下来m行,每行三个数,u,v,w,表示有一条从u到v。

输出描述:

输出m行,每行三个整数u,v,w表示有从u到v的边边权改完后是为w。
ps:按输入顺序输出。

题解

根据spfa的放缩原理 我们可以得知 一定是大于等于0的。

我们假设在新图上从u到v的路径权值为

假设在原图里u->v的最短路径为u->x1->x2->x3->v

那原图的最短路也就是

在我们更改了边权的新图中的最短路为:

也就是:

是一个定值,故我们更改权值之后最短路的节点顺序仍然不变。

所以我们只要把现在的边权规定为即可

代码

/*
* Coder: niiii
* Language: cpp
*/

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+100;
const int mod=1e9+7;

struct node{
    int now,to,net,w;
}s[N];

int head[N],dis[N],in[N];
int tot=0;

void add(int u,int v,int w){
    s[++tot]={u,v,head[u],w};
    head[u]=tot;
}

void spfa(){
    queue<int> Q;
    in[0]=1;
    dis[0]=0;
    Q.push(0);
    while(!Q.empty()){
        int now=Q.front();
        Q.pop();
        in[now]=0;
        for(int i=head[now];~i;i=s[i].net){
            int to=s[i].to;
            if(dis[to]>dis[now]+s[i].w){
                dis[to]=dis[now]+s[i].w;
                if(!in[to]){
                    Q.push(to);
                    in[to]=1;
                }
            }
        }
    }
}

int main(){
    ios::sync_with_stdio(false);
    int n,m;
    memset(dis,0x3f,sizeof(dis));
    memset(head,-1,sizeof(head));
    cin>>n>>m;
    for(int i=1;i<=m;++i){
        int u,v,w;
        cin>>u>>v>>w;
        add(u,v,w);
    }
    for(int i=1;i<=n;++i){
        add(0,i,0);
    }
    spfa();
    for(int i=1;i<=m;++i){
        cout<<s[i].now<<" "<<s[i].to<<" "<<dis[s[i].now]-dis[s[i].to]+s[i].w<<endl;
    }
}

/*
(u,x1)+(x1,x2)+(x2,x3)+(x2,t)
*/