zoukankan      html  css  js  c++  java
  • 【JZOJ1914】【2011集训队出题】最短路

    题目大意
    给你一个带权无向图,满足图上任意一条边最多属于一个环,有(q)个询问,求(u,v)之间的最短路。
    (n,qleq 10000)

    Solution

    首先用Tarjan建一棵以(1)为根的搜索树,找出每个环,记录环的总长,将环内每个点(u)连向环内(dfs)序最小的点(v),边权为(u)(v)的最短路,然后把不在环上的边照旧连上,这样我们就得到了一棵树。
    现在要求(a)(b)的最短路,我们考虑倍增,若两个点在一条链上,它们的最短路就是树上的距离。若两个点不在一条链上,设(u)(v)分别为(a)(b)(lca)的链上(lca)下面那个点,若(u,v)在原图中不在一个环,(a,b)的最短路就是它们在树上的距离,若(u,v)在原图中在一个环,则(u)(v)有两种走法,我们要取最小值,然后再加上(a)(u)的距离和(b)(v)的距离。
    这里有个小技巧,我们可以先在原图上从(1)开始求出(1)到每个点的最短路(dis[i]),那么将环上每个点(u)连向(dfs)序最小的点(v)时,边权就是(dis[u]-dis[v]),后面求树上两点距离时也是用(dis)数组求。

    Code

    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    typedef long long ll;
    const int N=10007,M=600007;
    ll Abs(ll a){return a>0?a:-a;}
    
    int n,m,q;
    
    int tot,cnt,st[N],to[M],nx[M],dep[N],col[N];
    ll len[M],dis[N],sum[N],totlen[N];
    vector<int> son[N];
    void add(int u,int v,ll w){to[++tot]=v,nx[tot]=st[u],len[tot]=w,st[u]=tot;}
    
    int head,tail,que[M],vis[N];
    void spfa(){
    	memset(dis,0x3f,sizeof(dis));memset(vis,0,sizeof(vis));
    	head=1,que[tail=1]=1,dis[1]=0,vis[1]=1;
    	while(head<=tail){
    		int u=que[head++];vis[u]=0;
    		for(int i=st[u];i;i=nx[i])if(dis[u]+len[i]<dis[to[i]]){
    			dis[to[i]]=dis[u]+len[i];
    			if(!vis[to[i]])que[++tail]=to[i],vis[to[i]]=1;
    		}
    	}
    }
    
    int tid,dfn[N],anc[N][15];
    void dfs(int u,int from){
    	dfn[u]=++tid;
    	for(int i=st[u];i;i=nx[i]){
    		if(!dfn[to[i]])anc[to[i]][0]=u,sum[to[i]]=sum[u]+len[i],dfs(to[i],u);
    		else if(to[i]!=from&&dfn[to[i]]<dfn[u]){
    			++cnt,totlen[cnt]=sum[u]-sum[to[i]]+len[i];
    			for(int j=u,t;j!=to[i];)son[to[i]].push_back(j),col[j]=cnt,t=anc[j][0],anc[j][0]=to[i],j=t;
    		}
    	}
    	if(anc[u][0]==from)son[from].push_back(u);
    }
    
    void dfs1(int u){
    	for(int i=0;i<son[u].size();++i)dep[son[u][i]]=dep[u]+1,dfs1(son[u][i]);
    }
    
    ll qry(int u,int v){
    	if(dep[u]<dep[v])swap(u,v);
    	int a=u,b=v;
    	for(int i=14;i>=0;--i)if(dep[anc[u][i]]>=dep[v])u=anc[u][i];
    	if(u==v)return dis[a]-dis[b];
    	for(int i=14;i>=0;--i)if(anc[u][i]!=anc[v][i])u=anc[u][i],v=anc[v][i];
    	if(col[u]&&col[u]==col[v])return dis[a]-dis[u]+dis[b]-dis[v]+min(Abs(sum[u]-sum[v]),totlen[col[u]]-Abs(sum[u]-sum[v]));
    	return dis[a]+dis[b]-2*dis[anc[u][0]];
    }
    
    int main(){
    	scanf("%d%d%d",&n,&m,&q);
    	for(int i=1;i<=m;++i){
    		int u,v;ll w;
    		scanf("%d%d%lld",&u,&v,&w),add(u,v,w),add(v,u,w);
    	}
    	spfa();
    	dfs(1,1);
    	dep[1]=1,dfs1(1);
    	for(int j=1;j<=14;++j)for(int i=1;i<=n;++i)anc[i][j]=anc[anc[i][j-1]][j-1];
    	while(q--){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		printf("%lld
    ",qry(u,v));
    	}
    	return 0;
    }
    
  • 相关阅读:
    1130
    Oracle 数据库常用操作语句大全
    Oracle用sys登陆报:ORA-28009:connection as sys should be as sysdba
    导出数据报ORA-39002: 操作无效 ORA-39070: 无法打开日志文件。 ORA-39087: 目录名 DUMP_DIR 无效
    SGI STL源码stl_bvector.h分析
    SGI STL源码stl_vector.h分析
    CGI 萃取技术 __type_traits
    迭代器iterator和traits编程技法
    智能指针分析及auto_ptr源码
    C++深拷贝和浅拷贝细节理解
  • 原文地址:https://www.cnblogs.com/zjlcnblogs/p/12088697.html
Copyright © 2011-2022 走看看