[模板] 基础树上问题
一、树的直径
定义:给定一棵树,树种最远的两个节点之间的距离被称为树的直径。
树的直径有两种求法,树形DP和两次BFS,时间复杂度都是O(n)。
树形DP求树的直径
设D[x]表示从节点x出发向下走,能够到达的最远节点的距离。设x的子节点为y,则有:
D[x]=max{D[\(y_i\)])+edge(x,yi)};
设F[x]为经过节点x的最长链的长度。 对于x的任意两个子节点yi,yj,f[x]可以通过四个部分构成:从yi到yj子树中的最远距离,边(x,yi),边(x,yj),从yj到yi子树中的最远距离,即:
F[x]=max{D[yi]+D[yj]+edge(x,yi)+edge(x,yj)};
最后的答案为\(F[1]\),故只需求出\(D[1]\)即可。
这种算法的复杂度是\(n^2\)级别的,考虑优化。
可以发现,枚举第j个子节点边,即和前1~j-1个子节点进行结合,而D[yj]+edge(x,yj)为定值。
则可以用类似于单调队列的思想,用一个变量来记录前1~j-1中最大的D[yi]+edge(x,yi),这样就可以做到线性复杂度。
code:
void dp(int u)
{
vis[u]=1;
for(int i=h[u];i;i=e[i].nex)
{
int v=e[i].v;
if(!vis[v])
{
dp(v);
maxd=max(maxd,f[v]+f[u]+e[i].w);
f[u]=max(f[u],f[v]+e[i].w);
}
}
}
两次BFS求树的直径
1.从任意一个节点出发,通过DFS或BFS对树进行一次遍历,求出于出发点距离最远的节点,记为p。
2.从节点p出发,通过BFS或DFS再进行一次遍历,求出与p距离最远的节点,记为q。
从p到q的路径就是树的一条直径。(证明略过)
code:
int bfs(int s)
{
memset(dis,0x3f,sizeof(dis));
queue<int> q;
q.push(s);
dis[s]=pre[s]=0;
while(q.size())
{
int u=q.front();
q.pop();
for(int i=h[u];i;i=e[i].nex)
{
int v=e[i].v;
if(dis[v]==INF)
{
dis[v]=dis[u]+e[i].w;
pre[v]=i;
q.push(v);
}
}
}
int d=1;
for(int i=2;i<=n;i++)
if(dis[i]>dis[d]) d=i;
return d;
}
p=bfs(1);
q=bfs(p);
二、树的重心
设size[x]表示以x为根的子树的大小。
定义:对于一个节点p,如果把它从树中删去,那么原来的一棵树可能会分成若干个子树。设max_part(p)表示在删去p后,生成的子树中最大的子树的大小。则令max_part(p)最小的节点就称为树的重心。
code:
void dfs(int u)
{
vis[u]=true,size[u]=1;
int max_part=0;
for(int i=h[u];i;i=e[i].nex)
{
int v=e[i].v;
if(vis[v]) continue;
dfs(v);
size[u]+=size[v];
max_part=max(max_part,size[v]);
}
max_part=max(max_part,n-size[u]);
if(max_part<ans)
{
ans=max_part;
ansnum=u;
}
}
三、最近公共祖先(LCA)
定义:给定一棵有根树,若节点z满足z既是节点x的祖先,又是节点y的祖先,则称z为(x,y)的公共祖先,在(x,y)的所有公共祖先中,深度最大的一个被称为(x,y)的最近公共祖先,即LCA(x,y)。
树上倍增法
设d[x]表示x的深度。
设F[x][k]表示x的2k次辈祖先,即从x向根节点走2k步到达的节点。若该点不存在,则F[x][k]=0;
F[x][0]就是x的父节点。除此之外,F[x][k]=F[F[x][k-1]][k-1];
算法实现步骤:
1.设d[x]\(\ge\)d[y],若不满足可以交换x,y;
2.把x向上调整到与y同一深度。
3.若x=y,则LCA(x,y)=y;
4.把x,y同时向上跳,保持深度一致且二者不相会。
5.此时,F[x][0]或F[y][0] 即为LCA(x,y);
code:
#include<bits/stdc++.h>
using namespace std;
const int M=5e5+10;
int lg[M],n,m,s,h[M],idx,fa[M][20],depth[M];
struct edge{
int v,nex;
}e[M<<1];
void add(int u,int v)
{
e[++idx].v=v;
e[idx].nex=h[u];
h[u]=idx;
}
void dfs(int u,int f)
{
depth[u]=depth[f]+1,fa[u][0]=f;
for(int i=1;i<=lg[depth[u]];i++) fa[u][i]=fa[fa[u][i-1]][i-1];
for(int i=h[u];i;i=e[i].nex)
if(e[i].v!=f) dfs(e[i].v,u);
}
int lca(int x,int y)
{
if(depth[x]<depth[y]) swap(x,y);
while(depth[x]>depth[y]) x=fa[x][lg[depth[x]-depth[y]]-1];
if(x==y) return x;
for(int i=lg[depth[x]]-1;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main()
{
cin>>n>>m>>s;
for(int x,y,i=1;i<n;i++)
{
cin>>x>>y;
add(x,y),add(y,x);
}
for(int i=1;i<=n;i++) lg[i]=lg[i-1]+((1<<lg[i-1])==i);
dfs(s,0);
while(m--)
{
int x,y;
cin>>x>>y;
cout<<lca(x,y)<<endl;
}
return 0;
}

浙公网安备 33010602011771号