蒟蒻的倍增
今天蒟蒻蒻学了倍增呦
倍增,字面意思
1,2,4,8,16,32 … …
也就是一倍一倍地增加(曲解qwq)
说到倍增,要从一个看似和倍增没有关系的题开始讲起
洛谷的LCA模板题(LCA:最近公共祖先)
附上链接: https://www.luogu.com.cn/problem/P3379
看完题目再食用效果更佳——鲁迅
以蒟蒻的智商来看这道题,首先想到的肯定就是暴力了
先用一个fa[]数组把这棵树存起来
然后对于每一对需要检测的节点,
让他们一次一次往上找
直到找到那个最近的公共节点
结果注定TLE
更加难受的是,如果所求两点i,j
i为j的祖先的时候,得到的答案是i的祖先而不是j的
#include<bits/stdc++.h>
using namespace std;
int fa[500005];
int father(int x)
{
while(x!=fa[x])
{
x=fa[x];
}
return x;
}
int main()
{
int n,m,s;
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<=s;i++) fa[i]=i;
for(int i=1;i<=n-1;i++)
{
int a,b;
scanf("%d%d",&a,&b);
fa[a]=b;
}
for(int i=1;i<=m;i++)
{
int aa,bb;
scanf("%d%d",&aa,&bb);
while(1)
{
if(aa!=bb)
{
aa=fa[aa];
bb=fa[bb];
}
else break;
}
printf("%d\n",aa);
}
return 0;
}
///原来的思路(暴力)
这样就需要用到倍增法优化的LCA了,需要用到链式前向星存树,因为树无向,所以要正反存两次,结构体数组也要开两倍。
edge,head,top都是存树用的
s为结点数
pp是倍增数组,有种dp的感觉
pp[i][j]=x中,i为当前节点,j为从i结点向上走2^j,x为所到达的节点
d存的是当前节点的深度
#include<bits/stdc++.h>
using namespace std;
const int N=500000+5;
struct node
{
int to,next;
}edge[N<<1];
int head[N];
int top=0;
int n,m,s;
int pp[N][22];///一般倍增到1<<20就够用了
int d[N];
int insert_edge(int x,int y)
{
edge[top].next=head[x];
edge[top].to=y;
head[x]=top++;
}///存图
void dfs(int now,int fa)///
{
d[now]=d[fa]+1;
pp[now][0]=fa;
for(int i=1;(1<<i)<=d[now];i++)
{
pp[now][i]=pp[pp[now][i-1]][i-1];
}
for(int i=head[now];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(to!=fa)
dfs(to,now);
}
}
int lca(int a,int b)
{
if(d[a]>d[b])
swap(a,b);
for(int i=20;i>=0;i--)///一定要从大往小找
{
if(d[a]<=d[b]-(1<<i))
{
b=pp[b][i];
}
}
if(a==b) return a;
for(int i=20;i>=0;i--)
{
if(pp[a][i]==pp[b][i])
continue;
else
{
a=pp[a][i];
b=pp[b][i];
}
}
return pp[a][0];
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
memset(head,-1,sizeof(head));
for(int i=1;i<=n-1;i++)
{
int a,b;
scanf("%d%d",&a,&b);
insert_edge(a,b);
insert_edge(b,a);
}
dfs(s,0);
while(m--)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",lca(a,b));
}
return 0;
}