點的距離 tarjanLCA模板題 P5020
阿新 • • 發佈:2018-11-30
Description
給定一個有n個結點的樹,Q個詢問,每次詢問點x與點y之間的最短距離。
Input
第一行一個n,接下來n-1行,每行兩個整數x,y,表示x,y之間有一條邊。然後是Q,接下來Q行每行兩個數x,y 表示詢問x到y的距離
Output
輸出Q行,每行鍼對每個詢問的結果
Hint
n,Q <=10^5
Solution
太坑了,我覺得我跟tarjan有仇。。。。他的演算法我從來都沒有獨立搞懂過啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。。。。。。我太弱了。。。
嗯首先就是求最近公共祖先LCA的話tarjan離線演算法就是把DFS跟並查集結合起來了(可惜我並查集全忘了。。。)然後大概思路就是把當前DFS到的點的父親結點全部更新成DFS的u這個點,然後如果這個時候已經找到了需要詢問的LCA就直接更新這個詢問的LCA的值,然後如果沒有的話就要把FA的值變成還要往上的父親結點,然後繼續去tarjan,最後就能得到所有需要詢問的LCA,只不過的話必須要知道所有需要詢問的兩個點,所以tarjan是離線演算法。
注意事項: 1.n個結點的樹有n-1條邊所以輸入邊的時候迴圈結束n不取等。。。 2.這道題要求的是兩個結點之間的距離,所以求距離就是兩個結點的depppp的和-LCA的depppp值的二倍。 3.無向圖用靜態連結串列存的話他的序號是1 3 5 7…所以最後那個迴圈是for(int i=1;i<=qqq*2;i+=2)。。。 4.例行,我是傻逼。。。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define maxn 200005 using namespace std; struct Edge{ int u; int v; int next; }edge[maxn],q[maxn]; int first[maxn],last[maxn],qf[maxn],ql[maxn],FA[maxn],lcaaaa[maxn],depppp[maxn]; int node1,node2,n,qqq,x,y; bool Vis_edge[maxn]; void addedge(int u,int v){ edge[++node1]=(Edge){u,v,0}; if(first[u]==0)first[u]=node1; else edge[last[u]].next=node1; last[u]=node1; } void addquery(int u,int v){ q[++node2]=(Edge){u,v,0}; if(qf[u]==0)qf[u]=node2; else q[ql[u]].next=node2; ql[u]=node2; } int find_Faaaaa(int s){ if(FA[s]!=s){ FA[s]=find_Faaaaa(FA[s]); } return FA[s]; } void tarjannnnn(int x,int faa){ depppp[x]=depppp[faa]+1; FA[x]=x; Vis_edge[x]=true; for(int i=first[x];i;i=edge[i].next){ int v=edge[i].v; if(!Vis_edge[v]){ tarjannnnn(v,x); FA[v]=x; } } for(int j=qf[x];j;j=q[j].next){ int v=q[j].v; if(Vis_edge[v]){ lcaaaa[j]=find_Faaaaa(v); } if(j%2){ lcaaaa[j+1]=lcaaaa[j]; } else{ lcaaaa[j-1]=lcaaaa[j]; } } } int main(){ scanf("%d",&n); for(int i=1;i<n;i++){ scanf("%d%d",&x,&y); addedge(x,y); addedge(y,x); } scanf("%d",&qqq); for(int i=1;i<=qqq;i++){ scanf("%d%d",&x,&y); addquery(x,y); addquery(y,x); } tarjannnnn(1,0); for(int i=1;i<=qqq*2;i+=2){ printf("%d\n",depppp[q[i].u]+depppp[q[i].v]-2*depppp[lcaaaa[i]]); } return 0; }