【codevs2370】小机房的树——倍增法/链剖求LCA
这道题就是LCA的完全模版题了,之前学倍增的时候写过一次,但是太久没复习忘了......
滚回来重新学了一波,赶紧写篇博客记录一下~
倍增法求LCA的基本思路:先dfs预处理出每个节点i的祖先2^j表示为gr[i][j]、深度deep[i]以及到祖先2^j 的距离dis[i][j]。然后对于每个询问x和y的最近公共祖先,先钦定(?)y在x的下方,然后先让y跳到x这一层,而(1<<i)&d巧妙地运用位运算让y能恰好用最少步数跳到x这一层。如果此时x==y即说明x是y的祖先,可以直接返回答案;否则就从大到小枚举倍增,这个地方也是倍增法的核心,要好好理解其中的巧妙之处。
要注意的地方:
1,根节点为0;
2.gr[i][0]即为节点i的父节点;
3.当y跳到x这一层时要注意判断x是否为y的祖先;
4.倍增结束时要记得x和y还要跳到他们的父节点上,ans不要忘记加上dis[x][0]和dis[y][0]。
其他细节详见代码:
#include<cstdio> #include<cstring> #include<algorithm> const int M=5e4+10; using namespace std; struct point{ int to,next,w; }e[M*2]; int n,m,sum=0,u,v,wi,first[M],deep[M]; bool ok[M]; int gr[M][22],dis[M][22]; int read() { int ans=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-48;c=getchar();} return ans*f; } void add(int x,int y,int z) { sum++;e[sum].to=y;e[sum].w=z;e[sum].next=first[x];first[x]=sum; sum++;e[sum].to=x;e[sum].w=z;e[sum].next=first[y];first[y]=sum; } void dfs(int x) { ok[x]=1; for(int i=1;i<=20;i++){ if((1<<i)>deep[x])break; gr[x][i]=gr[gr[x][i-1]][i-1]; dis[x][i]=dis[x][i-1]+dis[gr[x][i-1]][i-1]; } for(int i=first[x];i>0;i=e[i].next){ int now=e[i].to; if(ok[now])continue; deep[now]=deep[x]+1; gr[now][0]=x; dis[now][0]=e[i].w; dfs(now); } } inline int lca(int x,int y) { int ans=0; if(deep[x]>deep[y])swap(x,y); int d=deep[y]-deep[x]; for(int i=0;i<=20;i++) if((1<<i)&d){ ans+=dis[y][i]; y=gr[y][i]; } if(x==y)return ans; for(int i=20;i>=0;i--){ if(gr[x][i]!=gr[y][i]){ ans+=dis[x][i]+dis[y][i]; x=gr[x][i];y=gr[y][i]; } } ans+=dis[x][0]+dis[y][0]; return ans; } int main() { memset(first,-1,sizeof(first)); n=read();int a,b; for(int i=1;i<=n-1;i++){ u=read();v=read();wi=read(); add(u,v,wi); } dfs(0); m=read(); while(m--){ a=read();b=read(); printf("%d\n",lca(a,b)); } return 0; }
2017/9/21:
今天跟着ccz学了一波树链剖分求lca,理解完之后发现链剖无论是时间还是空间都完踩倍增诶>.<
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 const int N=5e4+10; 5 int tot=0,first[N],n,m; 6 int dis[N]; 7 struct node{ 8 int ne,to,w; 9 }e[N*2]; 10 struct point{ 11 int son,fa,top,sz,dep; 12 }q[N]; 13 void read(int &x){ 14 int f=1;x=0;char c=getchar(); 15 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 16 while(c>='0'&&c<='9'){x=x*10+c-48;c=getchar();} 17 x*=f; 18 } 19 void ins(int u,int v,int w){ 20 e[++tot]=(node){first[u],v,w};first[u]=tot; 21 e[++tot]=(node){first[v],u,w};first[v]=tot; 22 } 23 void dfs(int x,int fa){ 24 point&w=q[x]; 25 w.sz=1; 26 for(int i=first[x];i;i=e[i].ne){ 27 int to=e[i].to; 28 if(to!=fa){ 29 point&u=q[to];dis[to]=dis[x]+e[i].w; 30 u.dep=w.dep+1;u.fa=x; 31 dfs(to,x);w.sz+=u.sz;if(q[w.son].sz<u.sz)w.son=to; 32 } 33 } 34 } 35 void dfs1(int x,int tp){ 36 point&w=q[x];w.top=tp; 37 for(int i=first[x];i;i=e[i].ne){ 38 int to=e[i].to; 39 if(to!=w.fa){ 40 if(to==w.son)dfs1(to,tp); 41 else dfs1(to,to); 42 } 43 } 44 } 45 int lca(int x,int y){ 46 int a=q[x].top,b=q[y].top; 47 while(a!=b){ 48 if(q[a].dep>q[b].dep)x=q[a].fa,a=q[x].top; 49 else y=q[b].fa,b=q[y].top; 50 } 51 if(q[x].dep<q[y].dep)return x; 52 return y; 53 } 54 int main(){ 55 read(n); 56 for(int i=1,a,b,c;i<n;i++){ 57 read(a);read(b);read(c);a++;b++;ins(a,b,c); 58 } 59 dis[1]=0;dfs(1,0);dfs1(1,1); 60 read(m); 61 while(m--){ 62 int u,v;read(u);read(v);u++;v++; 63 printf("%d\n",dis[u]+dis[v]-2*dis[lca(u,v)]); 64 } 65 return 0; 66 }