洛谷 P3379 【模板】最近公共祖先(LCA,树链剖分)
传送门
解题思路
求LCA一般可以用倍增、树链剖分、tarjan算法解决。
这里只介绍树剖。
先处理出每一条链的链顶,然后对于每两个要求的点,判断是否在同一条链上。如果在,那么深度较浅的点就是LCA。
如果不在同一条链上,就比较两个所在链的链顶的深度,把链顶深度较低的点跳到链顶的父亲(这样就保证有答案了)。
时间复杂度:O(mlogn)。
AC代码
1 #include<iostream> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstdio> 5 #include<cstring> 6 using namespace std; 7 const int maxn=500005; 8 int n,m,s,siz[maxn],cnt,tp[maxn],dep[maxn],p[maxn],son[maxn],f[maxn]; 9 struct node{ 10 int v,next; 11 }e[maxn*2]; 12 void insert(int u,int v){ 13 cnt++; 14 e[cnt].next=p[u]; 15 e[cnt].v=v; 16 p[u]=cnt; 17 } 18 void dfs1(int u,int fa,int deep){ 19 int maxsize=0,maxson=0; 20 f[u]=fa; 21 siz[u]=1; 22 dep[u]=deep; 23 for(int i=p[u];i!=-1;i=e[i].next){ 24 int v=e[i].v; 25 if(v==fa) continue; 26 dfs1(v,u,deep+1); 27 siz[u]+=siz[v]; 28 if(siz[v]>maxsize){ 29 maxsize=siz[v]; 30 maxson=v; 31 } 32 } 33 son[u]=maxson; 34 } 35 void dfs2(int u,int fa,int top){ 36 tp[u]=top; 37 if(son[u]){ 38 dfs2(son[u],u,top); 39 for(int i=p[u];i!=-1;i=e[i].next){ 40 int v=e[i].v; 41 if(v==fa||v==son[u]) continue; 42 dfs2(v,u,v); 43 } 44 } 45 } 46 int main() 47 { 48 memset(p,-1,sizeof(p)); 49 cin>>n>>m>>s; 50 for(int i=1;i<n;i++){ 51 int u,v; 52 scanf("%d%d",&u,&v); 53 insert(u,v); 54 insert(v,u); 55 } 56 dfs1(s,-1,1); 57 dfs2(s,-1,s); 58 // for(int i=1;i<=n;i++) cout<<tp[i]<<" "; 59 for(int i=1;i<=m;i++){ 60 int a,b; 61 scanf("%d%d",&a,&b); 62 while(tp[a]!=tp[b]){ 63 if(dep[tp[a]]>dep[tp[b]]) a=f[tp[a]]; 64 else b=f[tp[b]]; 65 } 66 if(dep[a]>dep[b]) cout<<b<<endl; 67 else cout<<a<<endl; 68 } 69 return 0; 70 }