最近做了二叉树类型的题目,做了洛谷的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];
}
}
不知道我这个蒟蒻讲的能不能看懂。
具体怎么选择就看你们怎么选择辣!逃