今天学LCA,先照一个模板学习代码,给一个离线算法,主要方法是并查集加上递归思想。
再搞,第一个离线算法是比较常用了,基本离线都用这种方法了,复杂度O(n+q)。通过递归思想和并查集来寻找最近公共祖先,自己模拟下过程就可以理解了。
然后就是在线算法,在线算法方法就很多了,比较常用的是LCA的RMQ转换,然后还有线段树,DP等,最后效率最高的就是倍增法了每次查询O(LogN)
这道题是离线的。
给出离线的Tarjan和倍增算法吧。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; #define MAXN 10001 int f[MAXN]; int r[MAXN]; int indegree[MAXN]; int vis[MAXN]; vector<int>hash[MAXN],Qes[MAXN]; int ancestor[MAXN]; void init(int n) { int i; for(int i=1;i<=n;i++) { r[i]=1; f[i]=1; indegree[i]=0; vis[i]=0; ancestor[i]=0; hash[i].clear(); Qes[i].clear(); } } int find(int n) { if(f[n]!=n) f[n]=find(f[n]); return f[n]; } int Union(int x,int y) { int a=find(x); int b=find(y); if(a==b) return 0; else if(r[a]<r[b]) { f[a]=b; r[b]+=r[a]; } else { f[b]=a; r[a]+=r[b]; } return 1; } void LCA(int u) { ancestor[u]=u; int size=hash[u].size(); for(int i=0;i<size;i++) { LCA(hash[u][i]); Union(u,hash[u][i]); ancestor[find(u)]=u; } vis[u]=1; size=Qes[u].size(); for(int i=0;i<size;i++) { if(vis[Qes[u][i]]==1) { printf("%d ",ancestor[find(Qes[u][i])]); return ; } } } int main() { int T,s,t,n; scanf("%d",&T); while(T--) { scanf("%d",&n); init(n); for(int i=1;i<=n-1;i++) { scanf("%d%d",&s,&t); hash[s].push_back(t); indegree[t]++; } scanf("%d%d",&s,&t); Qes[s].push_back(t); Qes[t].push_back(s); for(int j=1;j<=n;j++) { if(indegree[j]==0) { LCA(j); break; } } } return 0; }
倍增法:
#include<iostream> #include<cstring> #include<cstdio> #include<vector> using namespace std; const int N=10002; const int Log=20; int dp[N][Log],depth[N],deg[N]; struct Edge { int to; Edge *next; }edge[2*N],*cur,*head[N]; void addedge(int u,int v) { cur->to=v; cur->next=head[u]; head[u]=cur++; } void dfs(int u) { depth[u]=depth[dp[u][0]]+1; for(int i=1;i<Log;i++) dp[u][i]=dp[dp[u][i-1]][i-1]; for(Edge *it=head[u];it;it=it->next) { dfs(it->to); } } int lca(int u,int v) { if(depth[u]<depth[v])swap(u,v); for(int st=1<<(Log-1),i=Log-1;i>=0;i--,st>>=1) { if(st<=depth[u]-depth[v]) { u=dp[u][i]; } } if(u==v) return u; for(int i=Log-1;i>=0;i--) { if(dp[v][i]!=dp[u][i]) { v=dp[v][i]; u=dp[u][i]; } } return dp[u][0]; } void init(int n) { for(int i=0;i<=n;i++) { dp[i][0]=0; head[i]=NULL; deg[i]=0; } cur=edge; } int main() { int T; scanf("%d",&T); while(T--) { int n,u,v; scanf("%d",&n); init(n); for(int i=0;i<n-1;i++) { scanf("%d%d",&u,&v); addedge(u,v); deg[v]++; dp[v][0]=u; } for(int i=1;i<=n;i++) { if(deg[i]==0) { dfs(i); break; } } scanf("%d%d",&u,&v); printf("%d ",lca(u,v)); } return 0; }