bzoj2125: 最短路

题目

Solution

思路
先对仙人掌图建圆方树,圆圆边和原图边权一致。对于每个方点代表的环,记深度最小的点为 x x x x x x为方点,圆方边的边权是圆点到 x x x的最短距离
l c a ( u , v ) lca(u,v) lca(u,v)为圆点,则两点间最短路转化为圆方树上 d i s [ u ] + d i s [ v ] − 2 ∗ d i s [ l c a ] dis[u]+dis[v]-2*dis[lca] dis[u]+dis[v]2dis[lca]。(向上延伸的路径,经过环则必然经过每个方点的 x x x,计算无误)
l c a ( u , v ) lca(u,v) lca(u,v)为方点,则记 u , v u,v u,v在方点连接的圆点 A , B A,B A,B的子树内,那么两点间最短路为 d i s [ u ] + d i s [ v ] − ∣ d i s [ A ] − d i s [ B ] ∣ + d i s ( A , B ) dis[u]+dis[v]-|dis[A]-dis[B]|+dis(A,B) dis[u]+dis[v]dis[A]dis[B]+dis(A,B) d i s ( A , B ) dis(A,B) dis(A,B) A , B A,B A,B在环上的短侧路径

Code

这题代码不会实现,就看了这份代码
这题本来应该求点双的,但这份代码里先求桥再求出环,看了好久才懂
刚开始看的时候可能会疑惑:桥的个数不是一定比割点少吗,但是它下面有一个循环,能做到不重不漏
其中 d f n [ v ] > d f n [ u ] dfn[v]>dfn[u] dfn[v]>dfn[u]的意思就是: u u u已经通过 u u u的某个儿子将 v v v遍历到,而因为 u u u v v v有边,所以构成了一个环

#include<bits/stdc++.h>
using namespace std;
const int N=10002,M=200002;
struct node{
	int to,ne,w;
}e[M<<1],e2[M];
int n,m,q,i,tim,dfn[N],low[N],d[N],sz[N],num,tot,tot2,h[N],h2[N],fa[N][14],dis[N][14],cir[N],dep[N],x,y,z;
inline char gc(){
	static char buf[100000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
	int x=0,fl=1;char ch=gc();
	for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
	for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
	return x*fl;
}
inline void wri(int a){if(a<0)a=-a,putchar('-');if(a>=10)wri(a/10);putchar(a%10|48);}
inline void wln(int a){wri(a);puts("");}
void add(int x,int y,int z){e[++tot]=(node){y,h[x],z},h[x]=tot;}
void add2(int x,int y){e2[++tot2]=(node){y,h2[x],0},h2[x]=tot2;}
void circle(int u,int v,int w){
	int len=d[v]-d[u]+w;
	sz[++num]=len;
	for (int i=v;i!=u;i=fa[i][0]){
		add2(u,i);
		cir[i]=num;
		dis[i][0]=min(d[i]-d[u],len-d[i]+d[u]);
	}
}
void tarjan(int u){
	dfn[u]=low[u]=++tim;
	for (int i=h[u],v;i;i=e[i].ne)
		if ((v=e[i].to)!=fa[u][0]){
			if (!dfn[v=e[i].to]){
				fa[v][0]=u,d[v]=d[u]+e[i].w;
				tarjan(v);
				low[u]=min(low[u],low[v]);
			}else low[u]=min(low[u],dfn[v]);
			if (low[v]>dfn[u]) add2(u,v),dis[v][0]=e[i].w;//桥
		}
	for (int i=h[u],v;i;i=e[i].ne)
		if (fa[v=e[i].to][0]!=u && dfn[v]>dfn[u]) circle(u,v,e[i].w);
}
void dfs(int u){
	for (int i=1;i<14;i++) fa[u][i]=fa[fa[u][i-1]][i-1],dis[u][i]=dis[u][i-1]+dis[fa[u][i-1]][i-1];
	for (int i=h2[u],v;i;i=e2[i].ne)
		if ((v=e2[i].to)!=fa[u][0]) fa[v][0]=u,dep[v]=dep[u]+1,dfs(v);
}
int solve(int x,int y){
	int ans=0;
	if (dep[x]<dep[y]) swap(x,y);
	for (int i=13;~i;i--)
		if (dep[fa[x][i]]>=dep[y]) ans+=dis[x][i],x=fa[x][i];
	if (x==y) return ans;
	for (int i=13;~i;i--)
		if (fa[x][i]!=fa[y][i]) ans+=dis[x][i]+dis[y][i],x=fa[x][i],y=fa[y][i];
	if (cir[x] && cir[x]==cir[y]) return ans+min(abs(d[x]-d[y]),sz[cir[x]]-abs(d[x]-d[y]));
	return ans+dis[x][0]+dis[y][0];
}
int main(){
	n=rd(),m=rd(),q=rd();
	for (i=1;i<=m;i++) x=rd(),y=rd(),z=rd(),add(x,y,z),add(y,x,z);
	tarjan(1);
	dfs(1);
	for (;q--;) x=rd(),y=rd(),wln(solve(x,y));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值