BZOJ2125 最短路

原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2125

最短路

Description

给一个N个点M条边的连通无向图,满足每条边最多属于一个环,有Q组询问,每次询问两点之间的最短路径。

Input

输入的第一行包含三个整数,分别表示N和M和Q 下接M行,每行三个整数v,u,w表示一条无向边v-u,长度为w 最后Q行,每行两个整数v,u表示一组询问

Output

输出Q行,每行一个整数表示询问的答案

Sample Input

9 10 2
1 2 1
1 4 1
3 4 1
2 3 1
3 7 1
7 8 2
7 9 2
1 5 3
1 6 4
5 6 1
1 9
5 7

Sample Output

5
6

HINT

对于100%的数据,N<=10000,Q<=10000

题解

哈哈我也是做过仙人掌的人啦。

其实这是一道非常板的题,我们先将仙人掌建成一棵圆方树:

图片1.png

对于一个环,新建一个方点,与环上的圆点连接,边的权值为圆点与深度最浅的圆点的距离。

图片2.png

这个操作可以通过魔改的Tarjan\mathcal{Tarjan}来完成,正常的树边直接连即可。

建完图以后就可以使用树上的套路了,一发dfs\mathcal{dfs}下去统计离根的距离,顺便得到倍增数组(貌似圆方树必须要倍增LCA\mathcal{LCA},表示很气)。

现在我们就得到了一棵树,根据普通树的套路,点xx与点yy的距离便为disx+disy2×dislca(x,y)dis_x+dis_y-2\times dis_{lca(x,y)}

但这可是棵圆方树啊,怎么能跟普通的树一样呢?

上述式子只有在lca(x,y)lca(x,y)为圆点时成立,当f=lca(x,y)f=lca(x,y)为方点时,设txt_xffxx走一步的点,同理tyt_yffyy走一步得到的点,最终的答案为disx+disydistxdisty+dis(tx,ty)dis_x+dis_y-dis_{t_x}-dis_{t_y}+dis(t_x,t_y)dis(tx,ty)dis(t_x,t_y)tx,tyt_x,t_y在仙人掌环上的距离。

代码

代码非常简短。

#include<bits/stdc++.h>
using namespace std;
const int M=2e4+5;
struct sd{int to,len;};
int n,m,q,df,tot,dfn[M],low[M],dad[M],val[M],S[M],id[M],dep[M],J[M][21],dis[M],tx,ty;
vector<sd>mmp[M],T[M];
void loop(int f,int t,int w)
{
	int pre=w,cot=0,i;
	for(i=t;i!=dad[f];i=dad[i])S[i]=pre,pre+=val[i],id[i]=cot++;
	S[++tot]=S[f];S[f]=0;
	for(i=t;i!=dad[f];i=dad[i])T[tot].push_back((sd){i,min(S[tot]-S[i],S[i])}),T[i].push_back((sd){tot,min(S[tot]-S[i],S[i])});
}
void tarjan(int v,int f)
{
	dfn[v]=low[v]=++df;dad[v]=f;int to;
	for(int i=mmp[v].size()-1;i>=0;--i)
	{
		to=mmp[v][i].to;if(to==f)continue;
		if(!dfn[to])val[to]=mmp[v][i].len,tarjan(to,v),low[v]=min(low[v],low[to]);
		else low[v]=min(low[v],dfn[to]);
		if(low[to]>dfn[v])T[v].push_back((sd){to,mmp[v][i].len});
	}
	for(int i=mmp[v].size()-1;i>=0;--i)
	{
		to=mmp[v][i].to;
		if(dad[to]!=v&&dfn[to]>dfn[v])loop(v,to,mmp[v][i].len);
	}
}
void dfs(int v,int f,int d,int l)
{
	dep[v]=d,dis[v]=l,J[v][0]=f;int to;
	for(int i=1;(1<<i)<=d;++i)J[v][i]=J[J[v][i-1]][i-1];
	for(int i=T[v].size()-1;i>=0;--i)
	{
		to=T[v][i].to;if(to==f)continue;
		dfs(to,v,d+1,l+T[v][i].len);
	}
}
int lca(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);int d=dep[x]-dep[y];
	for(int i=0;(1<<i)<=d;++i)if(d&(1<<i))x=J[x][i];
	if(x==y)return x;
	for(int i=20;i>=0;--i)if((1<<i)<=dep[x]&&J[x][i]!=J[y][i])x=J[x][i],y=J[y][i];
	tx=x;ty=y;
	return J[x][0];
}
void in()
{
	int a,b,c;scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=m;++i)scanf("%d%d%d",&a,&b,&c),mmp[a].push_back((sd){b,c}),mmp[b].push_back((sd){a,c});
}
void ac()
{
	tot=n;tarjan(1,0);dfs(1,0,1,0);
	int x,y,f,ans;
	while(q--)
	{
		scanf("%d%d",&x,&y);f=lca(x,y);
		if(f<=n)printf("%d\n",dis[x]+dis[y]-2*dis[f]);
		else
		{
			ans=dis[x]-dis[tx]+dis[y]-dis[ty];
			if(id[tx]<id[ty])ans+=min(S[tx]+S[f]-S[ty],S[ty]-S[tx]);
			else ans+=min(S[ty]+S[f]-S[tx],S[tx]-S[ty]);
			printf("%d\n",ans);
		}
	}
}
int main(){in();ac();}
发布了379 篇原创文章 · 获赞 405 · 访问量 6万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览