「JOI 2020 Final」奥运公交 (最短路)
问题实际上就是要分别求(1-n)和(n-1)对于每一条边翻转之后的最短路
由于(m)的上限为(n^2),下面所说的( ext{Dijkstra})都是没有堆优化的板本,即(n^2)找最小点,(m)更新
以计算(1-n)为例
不妨先考虑计算删除每一条边((u,v,c))之后,1为源点的的最短路情况
我们知道从源点(S)出发的最短路可以用最短路图描述,而最短路图是一张拓扑图(fix:这道题含有0边,所以并不是,但是没有关系)
如果从最短路图中提取一棵树,那么显然只有这些树边需要考虑删除之后对于最短路的影响
对于这些边重新求最短路即可,复杂度为(O(n(m+n^2)))
如何考虑翻转一条边之后的贡献?
不妨再求出以(n)为结束的最短路,即求反图(n)为源点的答案
然后在两个最短路上查询一下即可得到(1-n)的最短路
同理得到(n-1)的答案
复杂度为(O(n(m+n^2)))
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair <int,int> Pii;
#define reg register
#define mp make_pair
#define pb push_back
#define Mod1(x) ((x>=P)&&(x-=P))
#define Mod2(x) ((x<0)&&(x+=P))
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
template <class T> inline void cmin(T& a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T& a,T b){ ((a<b)&&(a=b)); }
char IO;
template <class T=int> T rd(){
T s=0;int f=0;
while(!isdigit(IO=getchar())) f|=IO=='-';
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
bool Mbe;
const int N=210,M=5e4+10,INF=2e9+10;
int n,m;
int E[N][N],E2[N][N],EI[N][N],dis[N],vis[N];
void Dijkstra(int S){
rep(i,0,n) dis[i]=INF,vis[i]=0;
dis[S]=0;
while(1) {
int u=0;
rep(i,1,n) if(!vis[i] && dis[i]<dis[u]) u=i;
if(!u) break;
vis[u]=1;
rep(i,1,n) if(E[u][i]<INF) cmin(dis[i],dis[u]+E[u][i]);
}
}
int U[M],V[M],C[M],D[M];
int mk[M];
void dfs(int u) {
vis[u]=1;
rep(i,1,n) if(!vis[i] && E[u][i]<INF && dis[i]==dis[u]+E[u][i]) mk[EI[u][i]]=1,dfs(i);
}
int Res1[M][N],Res2[M][N];
ll Ans[M];
void Solve(int S,int Res[M][N]){
// 计算删除每条边之后,S为源点的最短路情况,放在Res[M][N]中
rep(i,1,n) rep(j,1,n) E[i][j]=INF,E2[i][j]=INF;
rep(i,1,m) {
int u=U[i],v=V[i],c=C[i];
if(E[u][v]>c) E2[u][v]=E[u][v],E[u][v]=c,EI[u][v]=i;
else if(E2[u][v]>c) E2[u][v]=c;
}
Dijkstra(S);
rep(i,1,n) vis[i]=0;
rep(i,1,m) mk[i]=0;
dfs(S);
rep(i,1,m) if(!mk[i]) rep(j,1,n) Res[i][j]=dis[j];
rep(i,1,m) if(mk[i]) {
swap(E[U[i]][V[i]],E2[U[i]][V[i]]);
Dijkstra(S);
rep(j,1,n) Res[i][j]=dis[j];
swap(E[U[i]][V[i]],E2[U[i]][V[i]]);
}
}
void Solve(){
Solve(1,Res1);
rep(i,1,m) swap(U[i],V[i]);
// 反图计算
Solve(n,Res2);
rep(i,1,m) swap(U[i],V[i]);
rep(i,1,m) {
int t=min(Res1[i][n],Res2[i][1]);
if(Res1[i][V[i]]<INF && Res2[i][U[i]]<INF) cmin(t,Res1[i][V[i]]+Res2[i][U[i]]+C[i]);
Ans[i]+=t; // 合并贡献
}
}
bool Med;
int main(){
//fprintf(stderr,"%.2lf
",(&Med-&Mbe)/1024.0/1024.0);
n=rd(),m=rd();
rep(i,1,m) U[i]=rd(),V[i]=rd(),C[i]=rd(),D[i]=rd();
ll ans=0;
rep(i,1,n) rep(j,1,n) E[i][j]=INF;
rep(i,1,m) cmin(E[U[i]][V[i]],C[i]);
Dijkstra(1),ans+=dis[n];
Dijkstra(n),ans+=dis[1];
// 计算1-n
Solve();
// 计算n-1
rep(i,1,m) U[i]=n-U[i]+1,V[i]=n-V[i]+1;
Solve();
rep(i,1,m) cmin(ans,Ans[i]+D[i]);
printf("%lld
",ans>=INF?-1:ans);
}