zoukankan      html  css  js  c++  java
  • 「JOI 2020 Final」奥运公交 (最短路)

    「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);
    }
    
    
  • 相关阅读:
    数据库级触发器
    Erstudio8.0怎么用?Erstudio8.0汉化版详细使用教程
    Excel 信息对比_数组版
    百万级数据查询优化(数据库)
    sql查询重复记录、删除重复记录方法大全
    工作表(Worksheet)基本操作应用示例
    cxgrid中,如何根据列名或字段名取得footer值
    cxGrid使用汇总
    Delphi XE5通过WebService开发Web服务端和手机客户端
    cxGrid 锁定列
  • 原文地址:https://www.cnblogs.com/chasedeath/p/14048208.html
Copyright © 2011-2022 走看看