思路是 构建圆方树 + 倍增 LCA + tarjan 求解仙人掌上的最短路,记录每个点所处的环的长度与到环 dfn 最小处的距离,与各点到圆方树树根的距离。
如果你考虑 LCA 是方点的情况代码如下:
//circle[i] 是 i 所处环的长度,newdis[i] 是 i 到圆方树根的距离。
int Tu, Tv, valu, valv;
for(auto Edge : T[lca]){
int to = Edge.first;
if(get_lca(to, u) == to) Tu = to, valu = Edge.second;
if(get_lca(to, v) == to) Tv = to, valv = Edge.second;
}
int w = min(circle[Tu] - valu - valv, valv + valu);
if(circle[Tu] - valu - (circle[Tu] - valv) > 0) w = min(circle[Tu] - valu - (circle[Tu] - valv), w);
if(circle[Tu] - valv - (circle[Tu] - valu) > 0) w = min(circle[Tu] - valv - (circle[Tu] - valu), w);
write(newdis[u] - newdis[Tu] - newdis[Tv] + newdis[v] + w);
这会出现 Tu, Tv 不同侧的问题,所以建议加入数组判断在两者到环顶是否有公共部分,然后特判。
if(fl[Tu] == fl[Tv]){
if(circle[Tu] - valu - (circle[Tu] - valv) > 0) w = min(circle[Tu] - valu - (circle[Tu] - valv), w);
if(circle[Tu] - valv - (circle[Tu] - valu) > 0) w = min(circle[Tu] - valv - (circle[Tu] - valu), w);
}
这个 fl 的判断实现并不难,tarjan 加入下述代码即可。
// dis 是到原仙人掌的根的距离
if(dis[x] - dis[u] < circle[u] - dis[x]) fl[x] = 1;