題目描述
給出 n 個點的一棵樹,多次詢問兩點之間的最短距離。輸入格式注意:邊是無向的。
所有節點的編號是 1,2,…,n。
第一行為兩個整數 n 和 m。n 表示點數,m 表示詢問次數;輸出格式下來 n−1 行,每行三個整數 x,y,k,表示點 x 和點 y 之間存在一條邊長度為 k;
再接下來 m 行,每行兩個整數 x,y,表示詢問點 x 到點 y 的最短距離。
樹中結點編號從 1 到 n。
共 m 行,對於每次詢問,輸出一行詢問結果。資料範圍
2≤n≤104,題目分析1≤m≤2×104,
0輸入樣例1
2 21 2 100
1 22 1
輸出樣例1
100100
輸入樣例2
3 21 2 10
3 1 15
1 23 2
輸出樣例2
1025
這是一道求最近公共祖先的模板題。
tarjan演算法求最近公共祖先:o(n+m) 離線演算法
在深度優先遍歷時,將所有點分成三大類:
(2號點集)表示已經訪問過且已經回溯完的點
(1號點集)表示正在進行訪問的節點
(0號點集)表示已經訪問結束的點
我們可以在遍歷1號點集中的點的時候,將2號點集中的點用並查集合並到其1號點集中的父節點上去。
如果有一對查詢a和b,a點在1號點集中,b點在2號點集中,那麼它們的最近公共祖先即為b節點所在連通塊的根節點。
然後我們再回過頭來看看這道題:我們可以用dist[i]表示i節點到根節點的距離。
a節點到b節點的距離即為dist[a]+dist[b]-dist[p]*2 //p為a和b的最近公共祖先
**如下
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ull unsigned long long
#define pii pair
#define x first
#define y second
using
namespace std;
const
int n=
1e4+
5,inf=
0x3f3f3f3f
;int h[n]
,e[n*2]
,w[n*2]
,ne[n*2]
,idx;
int dist[n]
,p[n]
;int st[n]
,ans[n*2]
;vector query[n]
;void
add(
int a,
int b,
int c)
//加邊函式
intfind
(int x)
//並查集模板函式
void
dfs(
int u,
int fa)
//dfs預處理出dist陣列
}void
tarjan
(int u)
}for
(pii t:query[u]
)//遍歷有關u節點的所有的查詢
if(st[t.x]==2
)//如果另乙個點已被遍歷過
st[u]=2
;//遍歷結束,將u歸入2號點集
}int
main()
for(
int i=
0;i)//記錄所有的查詢);
query[b]
.push_back()
;}}dfs(1
,-1)
;tarjan(1
);for(
int i=
0;i)//輸出答案
printf
("%d\n"
,ans[i]);
return0;
}
最近公共祖先 距離
給出 nn 個點的一棵樹,多次詢問兩點之間的最短距離。注意 邊是無向的。所有節點的編號是 1,2,n1,2,n 輸入格式第一行為兩個整數 nn 和 mm nn表示點數,mm 表示詢問次數 下來 n 1n 1 行,每行三個整數 x,y,kx,y,k 表示點 xx 和點 yy 之間存在一條邊長度為 kk...
LCA(最近公共祖先) Tarjan
在一棵沒有環的樹上,每個節點肯定有其父親節點和祖先節點,而最近公共祖先,就是兩個節點在這棵樹上深度最大的公共的祖先節點。換句話說,就是兩個點在這棵樹上距離最近的公共祖先節點。所以lca主要是用來處理當兩個點僅有唯一一條確定的最短路徑時的路徑。tarjan的演算法複雜度為 o n q 思路 每進入乙個...
Tarjan演算法求LCA(最近公共祖先)
lca的離線演算法。複雜度為o n q 這個演算法充分利用了dfs樹的結構。對於每個節點u,關於它的詢問 u,v 只有兩種。假設先dfs u 後dfs v 1 v在u的子樹內。此時lca u,v u.2 v不在u的子樹內。假設v在u的父親的另一棵子樹內。此時lca u,v father u 如果不滿...