对仙人掌建立圆方树,然后对边定权
对于圆点和圆点之间的边,是原来仙人掌上的桥,边权保持不变
对于圆点和方点之间的边,将圆方树看做以一个圆点为根的有根树之后,一个方点的父亲一定是一个圆点。对于这条方圆边,将边权设为\(0\)。
而对于这个方点连接的其他圆点来说,如果要从这个点走到方点的父亲并走出这一个环,在原仙人掌上会走最短的路径。那么这些圆方边的权值就是在原仙人掌上从这个圆点到对应方点的父亲的最短路径长度。
然后在圆方树上建立倍增数组
接着考虑每一个询问。
对于某一个询问\((x,y)\),如果它们在圆方树上的\(LCA\)是圆点,那么直接取这一段路径的权值和。如果它们的\(LCA\)是方点,意味着通过不断跳,\(x\)和\(y\)跳到了同一个环内,那么在这个环内就有两条路径可以选择。那么我们现在要求环上的这两个点的最短路径。
考虑在建立圆方树时记录在\(dfs\)树上每一个点的带权深度,这样可以轻松地算出一个点到其方点父亲的最短路是否经过了返祖边,并通过这个计算出两个点之间的距离。
#include<bits/stdc++.h>
#define PII pair < int , int >
#define st first
#define nd second
//This code is written by Itst
using namespace std;
inline int read(){
int a = 0;
char c = getchar();
bool f = 0;
while(!isdigit(c) && c != EOF){
if(c == '-')
f = 1;
c = getchar();
}
if(c == EOF)
exit(0);
while(isdigit(c)){
a = a * 10 + c - 48;
c = getchar();
}
return f ? -a : a;
}
const int MAXN = 2e4 + 7;
struct Edge{
int end , upEd , w;
}Ed[MAXN << 1];
vector < int > ch[MAXN];
int head[MAXN] , dis[MAXN] , dep[MAXN] , jump[MAXN][16][2] , cir[MAXN];
int N , M , Q , cntEd , cnt;
inline void addEd(int a , int b , int c){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
Ed[cntEd].w = c;
head[a] = cntEd;
}
PII dfs1(int x , int p){
dep[x] = dep[p] + 1;
PII cur(0 , 0);
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(Ed[i].end != p)
if(!dep[Ed[i].end]){
dis[Ed[i].end] = dis[x] + Ed[i].w;
PII t = dfs1(Ed[i].end , x);
if(t.st)
if(t.st == x){
ch[x].push_back(t.nd);
jump[t.nd][0][0] = x;
}
else{
ch[t.nd].push_back(x);
jump[x][0][0] = t.nd;
jump[x][0][1] = min(dis[x] - dis[t.st] , cir[t.nd] + dis[t.st] - dis[x]);
cur = t;
}
else{
ch[x].push_back(Ed[i].end);
jump[Ed[i].end][0][0] = x;
jump[Ed[i].end][0][1] = Ed[i].w;
}
}
else
if(dep[Ed[i].end] < dep[x]){
cur.st = Ed[i].end;
cur.nd = ++cnt;
cir[cnt] = dis[x] - dis[Ed[i].end] + Ed[i].w;
ch[cnt].push_back(x);
jump[x][0][0] = cnt;
jump[x][0][1] = min(Ed[i].w , cir[cnt] - Ed[i].w);
}
return cur;
}
void dfs2(int x){
for(int i = 1 ; jump[x][i - 1][0] ; ++i){
jump[x][i][0] = jump[jump[x][i - 1][0]][i - 1][0];
jump[x][i][1] = jump[x][i - 1][1] + jump[jump[x][i - 1][0]][i - 1][1];
}
for(int i = 0 ; i < ch[x].size() ; ++i){
dep[ch[x][i]] = dep[x] + 1;
dfs2(ch[x][i]);
}
}
inline int abss(int a){
return a < 0 ? -a : a;
}
int query(int x , int y){
int sum = 0;
if(dep[x] < dep[y])
swap(x , y);
for(int i = 15 ; i >= 0 ; --i)
if(dep[x] - (1 << i) >= dep[y]){
sum += jump[x][i][1];
x = jump[x][i][0];
}
if(x == y)
return sum;
for(int i = 15 ; i >= 0 ; --i)
if(jump[x][i][0] != jump[y][i][0]){
sum = sum + jump[x][i][1] + jump[y][i][1];
x = jump[x][i][0];
y = jump[y][i][0];
}
if(jump[x][0][0] <= N)
return sum + jump[x][0][1] + jump[y][0][1];
else{
bool f = 0;
int p = jump[x][0][0] , t = jump[x][1][0];
if(dis[x] - dis[t] > cir[p] + dis[t] - dis[x])
f ^= 1;
if(dis[y] - dis[t] > cir[p] + dis[t] - dis[y])
f ^= 1;
int l = f ? jump[x][0][1] + jump[y][0][1] : abss(jump[x][0][1] - jump[y][0][1]);
return sum + min(l , cir[p] - l);
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
N = cnt = read();
M = read();
Q = read();
for(int i = 1 ; i <= M ; ++i){
int a = read() , b = read() , c = read();
addEd(a , b , c);
addEd(b , a , c);
}
dfs1(1 , 0);
dep[1] = 1;
dfs2(1);
for(int i = 1 ; i <= Q ; ++i)
printf("%d\n" , query(read() , read()));
return 0;
}