「JOI 2020 Final」奧運公交 (最短路)
阿新 • • 發佈:2020-11-27
「JOI 2020 Final」奧運公交 (最短路)
問題實際上就是要分別求\(1-n\)和\(n-1\)對於每一條邊翻轉之後的最短路
由於\(m\)的上限為\(n^2\),下面所說的\(\text{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\n",(&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\n",ans>=INF?-1:ans); }