hdu 2586 How far away?(LCA模板題+離線tarjan演算法)
How far away ?
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 25408 Accepted Submission(s): 10111
For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road connecting house i and house j,with length k(0<k<=40000).The houses are labeled from 1 to n.
Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.
Sample Input 2 3 2 1 2 10 3 1 15 1 2 2 3 2 2 1 2 100 1 2 2 1
Sample Output 10 25 100 100 題目大意: 給你一棵樹,求任兩點的距離。 這是LCA模板題。 求出兩點的公共祖先ancst,然後dis[i,j]=depth[i]+depth[j]-depth[ancst]*2;其中depth是節點在有根樹中的深度。 離線tarjan演算法。。ctrlc ctrlv一段大佬的解釋吧。(來自zhouzhendong的cnblogs)
LCA_Tarjan
TarjanTarjan 演算法求 LCA 的時間複雜度為 O(n+q)O(n+q) ,是一種離線演算法,要用到並查集。(注:這裡的複雜度其實應該不是 O(n+q)O(n+q) ,還需要考慮並查集操作的複雜度 ,但是由於在多數情況下,路徑壓縮並查集的單次操作複雜度可以看做 O(1)O(1),所以寫成了 O(n+q)O(n+q) 。)
TarjanTarjan 演算法基於 dfs ,在 dfs 的過程中,對於每個節點位置的詢問做出相應的回答。
dfs 的過程中,當一棵子樹被搜尋完成之後,就把他和他的父親合併成同一集合;在搜尋當前子樹節點的詢問時,如果該詢問的另一個節點已經被訪問過,那麼該編號的詢問是被標記了的,於是直接輸出當前狀態下,另一個節點所在的並查集的祖先;如果另一個節點還沒有被訪問過,那麼就做下標記,繼續 dfs 。
當然,暫時還沒那麼容易弄懂,所以建議結合下面的例子和標算來看看。
(下面的集合合併都用並查集實現)
比如:8−1−14−138−1−14−13 ,此時已經完成了對子樹 11 的子樹 1414 的 dfsdfs 與合併( 1414 子樹的集合與 11 所代表的集合合併),如果存在詢問 (13,14)(13,14) ,則其 LCA 即 getfather(14)getfather(14) ,即 11 ;如果還存在由節點 1313 與 已經完成搜尋的子樹中的 節點的詢問,那麼處理完。然後合併子樹 1313 的集合與其父親 11 當前的集合,回溯到子樹 11 ,並深搜完所有 11 的其他未被搜尋過的兒子,並完成子樹 11 中所有節點的合併,再往上回溯,對節點 11 進行類似的操作即可。
大意就是,後根dfs一棵樹,如果子樹遍歷完了,就把它整個加入子樹根節點的父親節點的並查集中。 遇到詢問,就判斷它的另一點是否已經遍歷過了,如果是,就getfather。還是自己畫畫圖,應該不難懂吧。 第一次寫,比較粗糙,附AC程式碼:#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <string> #include <cmath> #include <queue> #include <deque> #include <stack> #include <map> #include <set> typedef long long LL; const int MAX_N=40000; const int MAX_M=200; const int INF=1000000000; struct tedge { int to,w,next; }; tedge edge[MAX_N*2+5]; int head1[MAX_N+5],cnt1; void addedge(int a,int b,int c) { edge[cnt1]=(tedge){b,c,head1[a]};head1[a]=cnt1++; edge[cnt1]=(tedge){a,c,head1[b]};head1[b]=cnt1++; } struct tquery { int to,next; int index; }; tquery query[MAX_M*2+5]; int head2[MAX_N+5],cnt2; void addquery(int a,int b,int i) { query[cnt2]=(tquery){b,head2[a],i};head2[a]=cnt2++; query[cnt2]=(tquery){a,head2[b],i};head2[b]=cnt2++; } int fa[MAX_N]; int getf(int x) { if(fa[x]==x) return x; else return fa[x]=getf(fa[x]); } int vis[MAX_N+5]; int depth[MAX_N+5]; int ans[MAX_M+5]; void LCA(int x,int pa,int dis) { for(int i=head1[x];i!=-1;i=edge[i].next) { int l=edge[i].to; if(l!=pa) { LCA(l,x,dis+edge[i].w); } } depth[x]=dis; for(int i=head2[x];i!=-1;i=query[i].next) { int l=query[i].to; if(vis[l]) { int ancst=getf(l); ans[query[i].index]=depth[l]+depth[x]-depth[ancst]*2; //printf("%d %d %d\n",l,x,ancst); } } fa[x]=pa;vis[x]=1; } void init() { memset(head1,-1,sizeof(head1));cnt1=0; memset(head2,-1,sizeof(head2));cnt2=0; memset(vis,0,sizeof(vis)); } int main() { int T; scanf("%d",&T); while(T--) { init(); int n,m; scanf("%d%d",&n,&m); for(int i=1,a,b,c;i<=n-1;i++) { scanf("%d%d%d",&a,&b,&c); addedge(a,b,c); } for(int i=1,a,b;i<=m;i++) { scanf("%d%d",&a,&b); addquery(a,b,i); } for(int i=1;i<=n;i++) fa[i]=i; LCA(1,-1,0); for(int i=1;i<=m;i++) printf("%d\n",ans[i]); } return 0; }View Code