1. 程式人生 > >用樹鏈剖分來寫LCA

用樹鏈剖分來寫LCA

ostream 第一次 pri def -- != dfs roo truct

當兩個點在一條鏈上,它們的LCA就是深度較小的那個點。

於是這種樹鏈剖分寫LCA的思想就是把要求的兩個點想辦法靠到一條鏈上。

而且要靠到盡量更優的一條鏈上(重鏈)。

做法:

預處理出每棵樹上的重鏈(size大的),每個點求出一個top,代表與這個點能靠到最近的一條重鏈的位置。

求LCA時兩個點分別向各自top移動,直到兩個點到一條鏈上,輸出深度較小的

細節見代碼

#include<cstdio>
#include<iostream>
#define MAXN 500001
using namespace std;
struct edge{int pre,other;}b[MAXN*2
]; struct node{int last,p,depth,h,child,top;}a[MAXN]; int cnt,N,M,x,y,l,root; void connect(int x1,int x2) { b[++cnt]=(edge){a[x1].last,x2}; a[x1].last=cnt; } void dfs1(int x1)                  //第一次dfs預處理size、深度,求出重鏈 (變量名錯了 h就是size) { a[x1].depth=a[a[x1].p].depth+1; a[x1].h=1
; for(int i=a[x1].last;i;i=b[i].pre) { int x2=b[i].other; if(!a[x2].p&&a[x1].p!=x2) { a[x2].p=x1; dfs1(x2); a[x1].h+=a[x2].h; if(a[a[x1].child].h<a[x2].h)a[x1].child=x2; } } } void dfs2(int
x1)                 //第二次dfs預處理top {  if(x1==a[a[x1].p].child)a[x1].top=a[a[x1].p].top;             else a[x1].top=x1; for(int i=a[x1].last;i;i=b[i].pre)if(a[b[i].other].p==x1)dfs2(b[i].other); } int LCA(int x1,int x2) { while(a[x1].top!=a[x2].top)           { if(a[a[x1].top].depth>a[a[x2].top].depth)x1=a[a[x1].top].p; else x2=a[a[x2].top].p;          //深度大的點向top移動 } return a[x1].depth<a[x2].depth?x1:x2; } int main() { scanf("%d%d%d",&N,&M,&root); for(int i=1;i<=N-1;i++) { scanf("%d%d",&x,&y); connect(x,y); connect(y,x); } dfs1(root); dfs2(root); while(M--) { scanf("%d%d",&x,&y); printf("%d\n",LCA(x,y)); } return 0; }

用樹鏈剖分來寫LCA