原题链接: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
题解
哈哈我也是做过仙人掌的人啦。
其实这是一道非常板的题,我们先将仙人掌建成一棵圆方树:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ovUdRAvo-1602208085019)(https://i.loli.net/2018/07/21/5b531d50f01df.png)]
对于一个环,新建一个方点,与环上的圆点连接,边的权值为圆点与深度最浅的圆点的距离。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k8Xq6vV5-1602208085022)(https://i.loli.net/2018/07/21/5b531d50efac0.png)]
这个操作可以通过魔改的 T a r j a n \mathcal{Tarjan} Tarjan来完成,正常的树边直接连即可。
建完图以后就可以使用树上的套路了,一发 d f s \mathcal{dfs} dfs下去统计离根的距离,顺便得到倍增数组(貌似圆方树必须要倍增 L C A \mathcal{LCA} LCA,表示很气)。
现在我们就得到了一棵树,根据普通树的套路,点 x x x与点 y y y的距离便为 d i s x + d i s y − 2 × d i s l c a ( x , y ) dis_x+dis_y-2\times dis_{lca(x,y)} disx+disy−2×dislca(x,y)。
但这可是棵圆方树啊,怎么能跟普通的树一样呢?
上述式子只有在 l c a ( x , y ) lca(x,y) lca(x,y)为圆点时成立,当 f = l c a ( x , y ) f=lca(x,y) f=lca(x,y)为方点时,设 t x t_x tx为 f f f向 x x x走一步的点,同理 t y t_y ty为 f f f向 y y y走一步得到的点,最终的答案为 d i s x + d i s y − d i s t x − d i s t y + d i s ( t x , t y ) dis_x+dis_y-dis_{t_x}-dis_{t_y}+dis(t_x,t_y) disx+disy−distx−disty+dis(tx,ty), d i s ( t x , t y ) dis(t_x,t_y) dis(tx,ty)为 t x , t y t_x,t_y tx,ty在仙人掌环上的距离。
代码
代码非常简短。
#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();}