「JOI 2020 Final」奥运公交 (最短路)

「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);
}

posted @ 2020-11-27 15:23  chasedeath  阅读(289)  评论(0编辑  收藏  举报