题解
考虑转化成树上问题,那么显然需要构建圆方树。原点和方点的距离如何定义呢?考虑在仙人掌上两个点找最短距离的过程,不考虑返祖边的话就是一直跳到lca,返祖变的作用就是在那个环上的时候有机会更快跳到环顶。于是对于每条返祖边对应的环,环上所有点到方点的距离先定位它到环顶端的最短距离。这样,如果两个点的lca是原点,那么直接计算;否则说明它们的lca在一个环上,多记录一下每个点在环上的编号即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10,M=17;
int n,m,q,c,t=1;
int h[N],to[N*2],nx[N*2],w[N*2];
void add(int u,int v,int x){
t++; to[t]=v; nx[t]=h[u]; h[u]=t; w[t]=x;
}
int dn[N],ds[N],l[N],fa[N],fl[N];
bool vi[N*2],is[N*2];
void tar(int u){
dn[u]=++c;
for(int e=h[u],v;e;e=nx[e]){
v=to[e]; if(vi[e]) continue; vi[e]=vi[e^1]=1;
if(!dn[v]) ds[v]=ds[u]+w[e],fa[v]=u,fl[v]=e,tar(v);
else if(dn[v]<dn[u]){
n++; l[n]=ds[u]-ds[v]+w[e];
add(n,v,0),add(v,n,0); is[e]=is[e^1]=1;
for(int i=u;i!=v;i=fa[i]){
int x=min(ds[i]-ds[v],l[n]-(ds[i]-ds[v]));
add(i,n,x); add(n,i,x);
is[fl[i]]=is[fl[i]^1]=1;
}
}
}
}
int dp[N],dis[N],f[N][M];
void dfs(int u){
for(int i=1;i<M;i++) f[u][i]=f[f[u][i-1]][i-1];
for(int v,e=h[u];e;e=nx[e]) if(!is[e] && to[e]!=f[u][0])
v=to[e],dp[v]=dp[u]+1,dis[v]=dis[u]+w[e],f[v][0]=u,dfs(v);
}
int find(int u,int v){ //cout<<u<<" "<<v<<endl;
if(dp[u]<dp[v]) swap(u,v);
for(int i=M-1;~i;i--) if(dp[f[u][i]]>=dp[v]) u=f[u][i]; //cout<<u<<" "<<v<<endl;
for(int i=M-1;~i;i--) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];//cout<<i<<endl;
return (u!=v)?f[u][0]:u;
}
int get(int u,int v){
for(int i=M-1;~i;i--) if(dp[f[u][i]]>dp[v]) u=f[u][i];
return u;
}
int main()
{
//freopen("G.in","r",stdin);
//freopen(".out","w",stdout);
cin>>n>>m>>q; int u,v,x;
for(int i=1;i<=m;i++){
scanf("%d %d %d",&u,&v,&x); add(u,v,x),add(v,u,x);
}
m=n; tar(1); dp[1]=1; dfs(1);
/*for(int i=1;i<=n;i++){
cout<<i<<" dis="<<dis[i]<<" dp="<<dp[i]<<" f0="<<f[i][0]<<" f1="<<f[i][1]<<endl;
}*/
while(q--){
scanf("%d %d",&u,&v);
int lca=find(u,v); //cout<<lca<<endl;
if(lca<=m) printf("%d\n",dis[u]+dis[v]-2*dis[lca]);
else{ //cout<<1<<endl;
int fu=get(u,lca),fv=get(v,lca);
int ans=dis[u]-dis[fu]+dis[v]-dis[fv];
int len=abs(ds[fu]-ds[fv]);
printf("%d\n",ans+min(len,l[lca]-len));
}
}
return 0;
}