二叉树の学习之最近公共祖先

最近做了二叉树类型的题目,做了洛谷的P3884 [JLOI2009]二叉树问题,学习了一种新的算法(倍增),最近公共祖先
题目链接:[P3884二叉树问题]
(https://www.luogu.com.cn/problem/P3884)
还有另外一道题:P3379 【模板】最近公共祖先(LCA)
感兴趣的可以看一下。
上代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define SZ(x) ((ll)x.size())
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define REP(i,n) for(int i=0;i<(n);++i)
#define mem(a,b) memset(a,(b),sizeof(a));
const int maxn = 300;
using namespace std;
typedef long long ll;
typedef vector<ll> vi;
typedef pair<ll,ll> pii;
template<class T>
inline void read(T &x){
 x=0;
 int f=0;
 char c=getchar();
 while(!isdigit(c)){
  f|=(c=='-');
  c=getchar();
 }
 while(isdigit(c)){
  x=(x<<3)+(x<<1)+c-'0';
  c=getchar();
 }
 if(f) x=-x;
}
ll ksm(ll a,ll b,ll mod){
 ll ans=1;
 while(b){
  if(b&1) ans=ans*a%mod;
  a=a*a%mod;
  b>>=1;
 }
 return ans;
}
ll gcd(ll a,ll b){
 return b==0?a:gcd(b,a%b);
}//前面的都是一些模板,代码从下面开始。 
struct node{
   int v,next;
}e[200<<1];//因为所有边是双向的,需要开两倍,100<<1,因为数据非常水,随便开的数组。 
int tot=0,head[maxn],n,lg[maxn],fa[maxn][10],deep[maxn],max1=0,max2;
//deep存储的是边的深度,lg定义为log(i)+1,是常数优化,算的时候方便一些。 
void add(int x,int y){
   e[++tot].v=y;
   e[tot].next=head[x];
   head[x]=tot;//注意一下,head[i]存储的是以i为起点的所有边中编号最大的那个,而作为起点开始遍历。 
}//链式前向星。 
void dfs(int now,int fat){//这里dfs是记录各个点的深度和他们2的i次方级的祖先,fa[i][j]存储的是点i的2的j次方级祖先。 
     fa[now][0]=fat;
     deep[now]=deep[fat]+1;
     max1=max(max1,deep[now]);
     for(int i=1;(1<<i)<=deep[now];i++)
     fa[now][i]=fa[fa[now][i-1]][i-1];//倍增的关键,简单点来说就是now的2^i祖先等于now的2^(i-1)祖先的2^(i-1)祖先
          // 2^i=2^i-1+2^i-1; 
    for(int i=head[now];i;i=e[i].next){
    if(e[i].v!=fat) dfs(e[i].v,now);
 }
}
int lca(int x,int y){
    if(deep[x]<deep[y]) swap(x,y);//始终保持x的深度大于y的深度。 
    while(deep[x]>deep[y]) x=fa[x][lg[deep[x]-deep[y]]-1];
 //这里使x跳到与y同一高度。
 //套用某个大佬的问答:
 //lg[dep[x]-dep[y]]表示log(dep[x]-dep[y])+1,再减去1 
    //让x往上跳2^log([dep[x]-dep[y])层 
    /*
    首先这里要明白一个知识点,是不是任何一个正整数都可以被一些2的n次方加起来,
 答案是肯定的,因为每个数都可以用2进制表示。
 所以倍增的意思就是以2的k次方跳跃。
 这里为什么可以使x一步一步接近y的那一层呢?
 设k=(deep[x]-deep[y])
 从大向小跳,层数分别有1,2,4,8等等, 
 假如deep[x]-deep[y]=5=4+1层 
 那么就需要向上跳两次,分别跳4层和1层
 向上跳了一次以后,deep[x]-deep[y]=5-4=1,再往上跳1层就可以了。
 deep[x]-deep[y]一步步减小,就达到同一层了。 
 */
    if(x==y) return x;//若x和y相等,则lca就是x了. 
    for(int i=lg[deep[x]]-1;i>=0;i--){
    if(fa[x][i]!=fa[y][i]){//x和y最后必定跳到lca的下一层,所以他们肯定不相等,如果不相等就跳过去。 
      x=fa[x][i];
      y=fa[y][i];
    }
  }
    return fa[x][0];
}
int kd[maxn];
int main(){
	scanf("%d",&n);
 	rep(i,1,n-1){
  	int x,y;
  	scanf("%d%d",&x,&y);
  	add(x,y);
  	add(y,x);
    }
    rep(i,1,n){
  	lg[i]=lg[i-1]+(1<<lg[i-1]==i);
    }
    dfs(1,0);
    int x,y;
    scanf("%d%d",&x,&y);
    for(int i=1;i<=n;i++) kd[deep[i]]++;
    for(int i=1;i<=max1;i++) max2=max(kd[i],max2);
    printf("%d\n%d\n",max1,max2);
    int k=lca(x,y);
    printf("%d",(deep[x]-deep[k])*2+(deep[y]-deep[k]));
    return 0;
}

当然,如果lca算法不想用lg数组还有另外一种写法,

for(int i=10;i>=0;i--){//为什么要用10呢,因为题目数据很水,i=10已经足够了。
    if(deep[y]<=deep[x]-(1<<i))
      x=fa[x][i];
 }
 if(x==y) return x;
 for(int i=10;i>=0;i--){
  if(fa[x][i]!=fa[y][i]){
     x=fa[x][i];
     y=fa[y][i];
  }
 }

不知道我这个蒟蒻讲的能不能看懂。
具体怎么选择就看你们怎么选择辣!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值