一.题意

给出 n 个点和 m条边的有向图,边权可能为负值,修改任意边权,使所有边权非负且任意<u,v>的最短路路径不变。

二.题解

关键点在于两个:

  1. 建图。最短路+负边权不难联想到 ,由于是单源最短路,所以要增加一个超级源点,保证原本的 n 个点都有对应的最短路。
  2. 边权。最短路松弛条件为 ,松弛形式为 ,所以跑完 后肯定成立的有对于任意 :

参考官方题解的证明:
以超级源点存在为前提,对于最短路 ,有:

保证所有的最短路方案都不变,我们考虑使得新的最短只与旧最短路以及起点终点有关,与其他的值都无关,
所以我们只需要在在最短路松弛中消去其他的值,考虑对于任意边,令其边权为
(上面已经证得为非负数),就可以保证对每条最短路的影响只与 有关。
对于原最短路 ,有:

三.代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define eps 1e-10
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=1e5+5;
const int N=5e3+5;
const int mod=1e9+7;

ll head[N],d[N];
bool vis[N];
struct node{
    ll next,v,w,u;
}a[manx];
ll n,m,s,e,k;
void add(ll u,ll v,ll w){
    a[++k].v=v; a[k].u=u; a[k].next=head[u]; a[k].w=w; head[u]=k;
}
void spfa(){
    queue<ll>q;
    for(int i=0;i<=n;i++) d[i]=inf,vis[i]=0;
    q.push(s); vis[s]=1; d[s]=0;
    while(q.size()){
        ll u=q.front(); q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=a[i].next){
            ll v=a[i].v,w=a[i].w;
            if(d[v]>d[u]+w){
                d[v]=d[u]+w;
                if(!vis[v]) q.push(v),vis[v]=1;
            }
        }
    }
}

int main(){
    io; cin>>n>>m;
    ll u,v,w;
    for(int i=1;i<=m;i++){
        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++){
        u=a[i].u,v=a[i].v,w=a[i].w;
        printf("%lld %lld %lld\n",u,v,d[u]-d[v]+w);
    }
    return 0;
}