什么是最近公共祖先?
在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点。
换句话说,就是两个点在这棵树上距离最近的公共祖先节点。
所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径。
常用来求LCA的算法有:Tarjan/DFS(离线),ST/倍增(在线)。
1,Tarjan
tarjan的算法复杂度为$O(n+q)$。
思路:每进入一个节点u的深搜,就把整个树的一部分看作以节点u为根节点的小树,再搜索其他的节点。每搜索完一个点后,如果该点和另一个已搜索完点为需要查询LCA的点,则这两点的LCA为另一个点的现在的祖先。
- 先建两个图,一个为树的各条边,另一个是需要查询最近公共祖先的两节点。
- 建好后,从根节点开始进行一遍深搜。
- 先把该节点u的father设为他自己(也就是只看大树的一部分,把那一部分看作是一棵树),搜索与此节点相连的所有点v,如果点v没被搜索过,则进入点v的深搜,深搜完后把点v的father设为点u。
- 深搜完一点u后,开始判断节点u与另一节点v是否满足求LCA的条件,满足则将结果存入数组中。
- 搜索完所有点,自动退出初始的第一个深搜,输出结果。
例.poj1470
传送:http://poj.org/problem?id=1470
题意:有n个点的树。m个询问,每个询问要求求出<u,v>的LCA。最终输出某个点作为询问中的LCA出现的次数。
分析:(题意描述有点不清喵喵喵???默认输入是父亲到儿子,然后手动记录入度,找到根节点。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<string> 6 #include<vector> 7 using namespace std; 8 const int maxn=910; 9 typedef pair<int,int> pii; 10 vector<pii> q[maxn]; 11 vector<int> mp[maxn]; 12 int ans[maxn*maxn],num[maxn*maxn]; 13 int f[maxn],vis[maxn],flag[maxn]; 14 int find(int x){ 15 if (f[x]==x) return x; 16 else return f[x]=find(f[x]); 17 } 18 void LCA(int root){ 19 f[root]=root; 20 vis[root]=1; 21 for (int i=0;i<mp[root].size();i++){ 22 int tmp=mp[root][i]; 23 if (vis[tmp]) continue; 24 LCA(tmp); 25 f[tmp]=root; 26 } 27 for (int i=0;i<q[root].size();i++){ 28 pii tmp=q[root][i]; 29 if (vis[tmp.first]) 30 ans[tmp.second]=find(tmp.first); 31 } 32 } 33 int main(){ 34 int n,m,x,y,kk; 35 while (~scanf("%d",&n)){ 36 memset(flag,0,sizeof(flag)); 37 for (int i=0;i<n;i++){ 38 scanf("%d:(%d)",&x,&kk); 39 while (kk--){ 40 scanf("%d",&y); 41 flag[y]=1; 42 mp[x].push_back(y); 43 //mp[y].push_back(x); 44 } 45 } 46 scanf("%d",&m); char ch; 47 for (int i=0;i<m;i++){ 48 cin >> ch; 49 scanf("%d %d)",&x,&y); 50 q[x].push_back({y,i}); 51 q[y].push_back({x,i}); 52 } 53 for (int i=1;i<=n;i++) vis[i]=0; 54 int root; 55 for (int i=1;i<=n;i++) if (!flag[i]){root=i;break;} 56 LCA(root); 57 memset(num,0,sizeof(num)); 58 for (int i=0;i<m;i++) num[ans[i]]++; 59 for (int i=1;i<=n;i++) 60 if (num[i]) printf("%d:%d ",i,num[i]); 61 } 62 return 0; 63 }
练习:
1.hdu2586 How far away ?
传送:http://acm.hdu.edu.cn/showproblem.php?pid=2586
题意:求树上两点的最短距离。
分析:LCA。
树上最短路:$ans=dist[u]+dist[v]-2*dist[LCA(u,v)]$。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=4e4+10; 4 typedef pair<int,int> pii; 5 struct node{ 6 int to,val; 7 }; 8 vector<node> mp[maxn]; 9 vector<pii> q[maxn]; 10 int ans[210],f[maxn],vis[maxn],dis[maxn],flag[maxn]; 11 int find(int x){ 12 if (f[x]==x) return x; 13 else return f[x]=find(f[x]); 14 } 15 void LCA(int root){ 16 f[root]=root; 17 vis[root]=1; 18 for (int i=0;i<mp[root].size();i++){ 19 node tmp=mp[root][i]; 20 if (vis[tmp.to]) continue; 21 dis[tmp.to]=dis[root]+tmp.val; 22 LCA(tmp.to); 23 f[tmp.to]=root; 24 } 25 for (int i=0;i<q[root].size();i++){ 26 pii tmp=q[root][i]; 27 if (vis[tmp.first]){ 28 ans[tmp.second]=dis[root]+dis[tmp.first]-2*dis[find(tmp.first)]; 29 } 30 } 31 } 32 int main(){ 33 int t,n,m,x,y,z; scanf("%d",&t); 34 while (t--){ 35 scanf("%d%d",&n,&m); 36 for (int i=1;i<=n;i++) mp[i].clear(),flag[i]=0,q[i].clear(),dis[i]=0,vis[i]=0; 37 for (int i=0;i<n-1;i++){ 38 scanf("%d%d%d",&x,&y,&z); 39 mp[x].push_back({y,z}); 40 mp[y].push_back({x,z}); 41 flag[y]=1; 42 } 43 for (int i=0;i<m;i++){ 44 scanf("%d%d",&x,&y); 45 q[x].push_back({y,i}); 46 q[y].push_back({x,i}); 47 } 48 int root; 49 for (int i=1;i<=n;i++) if (!flag[i]){root=i;break;} 50 LCA(root); 51 for (int i=0;i<m;i++) printf("%d ",ans[i]); 52 } 53 return 0; 54 }
2.hdu2874 Connections between cities
传送:http://acm.hdu.edu.cn/showproblem.php?pid=2874
题意:n个点,m条边。问树上两点间最短路。
分析:图是森林。LCA。(卡内存,mle了若干发。
需要判断未经过的点作为新的一个树,求解答案。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e4+5; 4 const int maxm=1e6+5; 5 typedef pair<int,int> pii; 6 struct node{ 7 int to,val,nxt; 8 }mp[maxm*2]; 9 struct node2{ 10 int to,id,nxt; 11 }q[maxm*2]; 12 int head[maxn],q_head[maxn]; 13 int ans[maxm],f[maxn],dis[maxn]; 14 bool vis[maxn]; 15 int tot,tot2; 16 void addedge(int x,int y,int z){ 17 mp[tot].to=y; 18 mp[tot].val=z; 19 mp[tot].nxt=head[x]; 20 head[x]=tot++; 21 } 22 void addquery(int x,int y,int id){ 23 q[tot2].to=y; 24 q[tot2].id=id; 25 q[tot2].nxt=q_head[x]; 26 q_head[x]=tot2++; 27 } 28 int find(int x){ 29 if (f[x]==x) return x; 30 else return f[x]=find(f[x]); 31 } 32 void LCA(int root){ 33 f[root]=root; 34 vis[root]=1; 35 for (int i=head[root];i!=-1;i=mp[i].nxt){ 36 node tmp=mp[i]; 37 if (vis[tmp.to]) continue; 38 dis[tmp.to]=dis[root]+tmp.val; 39 LCA(tmp.to); 40 f[tmp.to]=root; 41 } 42 for (int i=q_head[root];i!=-1;i=q[i].nxt){ 43 node2 tmp=q[i]; 44 if (vis[tmp.to]){ 45 if (dis[tmp.to]!=-1) ans[tmp.id]=dis[root]+dis[tmp.to]-2*dis[find(tmp.to)]; 46 } 47 } 48 } 49 int main(){ 50 int n,m,c,x,y,z; 51 while (~scanf("%d%d%d",&n,&m,&c)){ 52 //for (int i=1;i<=n;i++) head[i]=-1,q_head[i]=-1,flag[i]=false; 53 //for (int i=0;i<=c;i++) ans[i]=-1; 54 memset(head,-1,sizeof(head)); 55 memset(q_head,-1,sizeof(q_head)); 56 memset(ans,-1,sizeof(ans)); 57 memset(vis,0,sizeof(vis)); 58 tot=0;tot2=0; 59 for (int i=0;i<m;i++){ 60 scanf("%d%d%d",&x,&y,&z); 61 addedge(x,y,z); 62 addedge(y,x,z); 63 } 64 for (int i=0;i<c;i++){ 65 scanf("%d%d",&x,&y); 66 addquery(x,y,i); 67 addquery(y,x,i); 68 } 69 for (int i=1;i<=n;i++){ 70 if (!vis[i]){ 71 //for (int j=1;j<=n;j++) vis[j]=0,dis[j]=0; 72 memset(dis,-1,sizeof(dis)); 73 dis[i]=0; 74 LCA(i); 75 } 76 } 77 for (int i=0;i<c;i++){ 78 if (ans[i]==-1) printf("Not connected "); 79 else printf("%d ",ans[i]); 80 } 81 } 82 return 0; 83 }