【算法】最近公共祖先(LCA)
【题解】
点x,y到最近公共祖先z的距离之和相当于x,y到根的距离减去两倍z到根的距离,
即ans=dis[x]+dis[y]-2*dis[z]
记得边数组要开两倍!!!T_T
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<cstdio> #include<algorithm> using namespace std; const double eps=1e-3; const int maxn=1010; struct cyc{int from,to,w;}e[maxn*2]; int dis[maxn],deep[maxn],f[maxn][15],n,q,cnt,head[maxn]; bool vis[maxn]; void insert(int u,int v,int w) {cnt++;e[cnt].from=head[u];e[cnt].to=v;e[cnt].w=w;head[u]=cnt;} void dfs(int x) { vis[x]=1; for(int i=1;(1<<i)<=deep[x];i++) f[x][i]=f[f[x][i-1]][i-1]; for(int i=head[x];i;i=e[i].from) if(!vis[e[i].to]) { int now=e[i].to; deep[now]=deep[x]+1; dis[now]=dis[x]+e[i].w; f[now][0]=x; dfs(now); } } int lca(int x,int y) { if(deep[x]<deep[y])swap(x,y); int d=deep[x]-deep[y]; for(int i=0;(1<<i)<=d;i++) if((1<<i)&d)x=f[x][i]; if(x==y)return x; for(int i=10;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } int main() { scanf("%d%d",&n,&q); for(int i=1;i<n;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); insert(u,v,w); insert(v,u,w); } dfs(1); for(int i=1;i<=q;i++) { int p1,p2; scanf("%d%d",&p1,&p2); printf("%d\n",dis[p1]+dis[p2]-2*dis[lca(p1,p2)]); } return 0; }
也可以记录g[x][i],则不必减去多余的。
事实证明在一些简单的树上路径问题上,倍增比链剖更有优势。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=1010; struct edge{int v,from,w;}e[maxn*3]; int n,first[maxn],deep[maxn],f[maxn][20],g[maxn][20],v[maxn],tot,lca,q; void insert(int u,int v,int w) {tot++;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;} void dfs(int x,int fa) { for(int i=1;(1<<i)<=deep[x];i++) { f[x][i]=f[f[x][i-1]][i-1]; g[x][i]=g[x][i-1]+g[f[x][i-1]][i-1]; } for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa) { int y=e[i].v; f[y][0]=x; deep[y]=deep[x]+1; g[y][0]=e[i].w; dfs(y,x); } } int find(int x,int y) { int ans=0; if(deep[x]<deep[y])swap(x,y); int d=deep[x]-deep[y]; for(int i=0;(1<<i)<=deep[x];i++) if((1<<i)&d){ans+=g[x][i];x=f[x][i];} if(x==y)return ans; for(int i=20;i>=0;i--) if((1<<i)<=deep[x]&&f[x][i]!=f[y][i]) { ans+=g[x][i]+g[y][i]; x=f[x][i];y=f[y][i]; } lca=f[x][0]; return ans+g[x][0]+g[y][0]; } int main() { scanf("%d%d",&n,&q); for(int i=1;i<n;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); insert(u,v,w); insert(v,u,w); } dfs(1,-1); for(int i=1;i<=q;i++) { int u,v; scanf("%d%d",&u,&v); printf("%d\n",find(u,v)); } return 0; }