LCA最近公共祖先(朴素+倍增法)

来源:互联网 发布:图表数据区域格式 编辑:程序博客网 时间:2024/05/16 10:49

<span style="color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: bold; line-height: 1.42857143; background-color: rgb(255, 255, 255);">样例输入</span>

4Adam SamSam JoeySam MichealAdam Kevin3Sam SamAdam SamMicheal Kevin
样例输出
SamAdamAdam
给出n个父子关系,然后接着m个询问,问两者的最近公共祖先是哪个。

下面的是用的很直白的搜索。每次找到两点的深度,然后将两点升到相同的高度。升到相同的高度后继续一起向上升。直到升到祖先相同。算法复杂度是O(dep[u]+dep[v]) ,最大深度为n,所以算法复杂度为O(n);

听起来还不错是吧~

But..

还有m 个询问啊,m如果很大。酱紫肯定TLE啊。下面的代码就是T的。。。


不过,肯定会有办法解决的是吧~

#include <cstdio>#include <cstring>#include <cmath>#include <iostream>#include <vector>#include <map>#include <algorithm>#define read freopen("q.in","r",stdin)#define LL long long#define maxn 100005using namespace std;int fa[maxn],dep[maxn],dg[maxn];map<string,int> mp;map<int,string> remp;vector<int> vt[maxn];void dfs(int x,int p,int d){fa[x]=p;dep[x]=d;int i,j;for(i=0;i<vt[x].size();i++)dfs(vt[x][i],x,d+1);}int lca(int u,int v){int i,j;while(dep[u]>dep[v])u=fa[u];while(dep[v]>dep[u])v=fa[v];while(u!=v){u=fa[u];v=fa[v];}return u;}int main(){int n,u,v,cnt=1;int root,i,q;string father,son;memset(dg,0,sizeof(dg));scanf("%d",&n);while(n--){cin>>father>>son;if(!mp[father]){mp[father]=cnt;remp[cnt]=father;cnt++;}if(!mp[son]){mp[son]=cnt;remp[cnt]=son;cnt++;}u=mp[father];v=mp[son];vt[u].push_back(v);dg[v]++;}for(i=1;i<cnt;i++)if(dg[i]==0){root=i;break;}dfs(root,-1,0);scanf("%d",&q);while(q--){cin>>father>>son;int res=lca(mp[father],mp[son]);cout<<remp[res]<<endl;}}
那既然,一步一步的走太慢,干脆在计算机中还经常logn,那就利用这点来加快速度吧。

fa[k][v]表示每次向上走k步,k是2的幂,k最大取log(n),分别计算可以走的步伐的父亲,然后从最长的步伐开始尝试。这样就可以快很多辣~


#include <cstdio>#include <cstring>#include <cmath>#include <iostream>#include <vector>#include <map>#include <algorithm>#define read freopen("q.in","r",stdin)#define LL long long#define maxn 100005#define maxlog (log(maxn)/log(2))using namespace std;int fa[100][maxn],dep[maxn],dg[maxn];map<string,int> mp;map<int,string> remp;vector<int> vt[maxn];int cnt,root;void dfs(int x,int p,int d){fa[0][x]=p;dep[x]=d;int i,j;for(i=0;i<vt[x].size();i++)dfs(vt[x][i],x,d+1);}void init(){int i,mlog=(log(cnt)/log(2));dfs(root,-1,0);for(int k=0;k+1<mlog;k++){for(int v=0;v<cnt;v++){if(fa[k][v]<0)fa[k+1][v]=-1;else fa[k+1][v]=fa[k][fa[k][v]];}}}int lca(int u,int v){int i,j,mlog=(log(cnt)/log(2));;if(dep[u]>dep[v])swap(u,v);for(i=0;i<mlog;i++){if((dep[v]-dep[u])>>i &1)v=fa[i][v];}if(u==v)return u;for(int k=mlog-1;k>=0;k--){if(fa[k][u]!=fa[k][v]){u=fa[k][u];v=fa[k][v];}}return fa[0][u];}int main(){//read;int n,u,v;cnt=1;int i,q;string father,son;memset(dg,0,sizeof(dg));scanf("%d",&n);while(n--){cin>>father>>son;if(!mp[father]){mp[father]=cnt;remp[cnt]=father;cnt++;}if(!mp[son]){mp[son]=cnt;remp[cnt]=son;cnt++;}u=mp[father];v=mp[son];vt[u].push_back(v);dg[v]++;}for(i=1;i<cnt;i++)if(dg[i]==0){root=i;break;}init();scanf("%d",&q);while(q--){cin>>father>>son;int res=lca(mp[father],mp[son]);cout<<remp[res]<<endl;}}

其实,解决lca问题的算法很多,除了这两种之外还有

一种叫tajian的算法,以及转化成RMQ的问题。

这些算法,以后再写。一篇博客不能太长。不然就太无趣辣,反正我忍受不了太长的博客。。。

0 0