用樹鏈剖分來寫LCA
阿新 • • 發佈:2017-09-29
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(intx1) //第二次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