BSOJ1620 -- 【LCA练习】最近公共祖先(版本2)3587
类似于luogu P3379 【模板】最近公共祖先(LCA)
Description
给你一棵有根树,要求你计算出指定两个结点的最近公共祖先。
Input
输入文件的第一行为结点个数n(2<=N<=200,000),结点编号为1到n
接下来n-1行,每行两个整数,第一个数字是第二个数字的父亲结点
接下来的一行为两个整数a和b,要求计算出结点a和b的最近公共祖先。
接下来n-1行,每行两个整数,第一个数字是第二个数字的父亲结点
接下来的一行为两个整数a和b,要求计算出结点a和b的最近公共祖先。
Output
输出文件仅一行为最近公共祖先的编号。
Sample Input
5
2 3
3 4
3 1
1 5
3 5
Sample Output
3
爬树法(树上倍增):
动态做法,边输入边找树根(套路)
预处理出p[i][k]代表i的第k代祖先,d[i]代表节点i的深度
之后用LCA爬树法重点处理:
1)使d[L]>d[R],否则交换,方便后面处理
2)计算出最多跳跃深度k=log2(d[L])
3)L跳到与R深度相同
4)L,R同时跳到LCA的子节点处
5)计算出LCA
代码如下:
#include<bits/stdc++.h> using namespace std; #define why 200005 struct starr { int next,to; }a[why<<1]; int n,root,p[why][18],d[why],prt[why],h[why],cnt; inline void add(int x,int y) { cnt++; a[cnt].to=y; a[cnt].next=h[x]; h[x]=cnt; } inline void DFS(int x,int num) { register int u; d[x]=num; for(u=h[x];u;u=a[u].next) { int y=a[u].to; DFS(y,num+1); } } inline void ST() { register int i,j; for(i=1;i<=n;i++)p[i][0]=prt[i]; for(j=1;(1<<j)<=n;j++) { for(i=1;i<=n;i++) { if(p[i][j-1]==-1)continue; p[i][j]=p[p[i][j-1]][j-1]; } } } inline int LCA(int L,int R) { register int i; int k; if(L==R)return L; if(d[L]<d[R])swap(L,R); k=int(log(d[L])/log(2)); for(i=k;i>=0;i--) { if(d[L]-(1<<i)>d[R])L=p[L][i]; else if(d[L]-(1<<i)==d[R]) { L=p[L][i]; break; } } if(L==R)return L; for(i=k;i>=0;i--) { if(p[L][i]!=-1&&p[L][i]!=p[R][i]) { L=p[L][i]; R=p[R][i]; } } return p[L][0]; } inline int redn() { int ret=0,f=1; char ch=getchar(); while(!isdigit(ch)) { if(ch=='-')f=-f; ch=getchar(); } while(isdigit(ch)) { ret=ret*10+ch-'0'; ch=getchar(); } return f>0?ret:-ret; } int main() { int _1,_2; register int i; n=redn(); memset(p,-1,sizeof(p)); for(i=1;i<n;i++) { _1=redn(); _2=redn(); prt[_2]=_1; add(_1,_2); if(i==1||_2==root)root=_1; } DFS(root,1); ST(); _1=redn(); _2=redn(); printf("%d",LCA(_1,_2)); return 0; }
Tarjan离线求LCA(待填)
RMQ/LCT求LCA 有兴趣再说
Orchideus Is Being Adorable.QwQ