树上倍增
倍增算法就是通过倍增快速查找的算法。它在树的问题上比树链剖分处理得要快而且简单。
它的核心公式就是fa[x][j]=fa[fa[x][j-1]][j-1] / fa[x][j+1]=fa[fa[x][j]][j].为了更好地理解这个公式,我打了以下表格:
0 | 1 | 2 | 3 | |
1 | 0 | 0 | 0 | 0 |
2 | 1(1+2^0) | 0 | 0 | 0 |
3 | 2 | 1(1+2^1) | 0 | 0 |
4 | 3 | 2 | 0 | 0 |
5 | 4 | 3 | 1(1+2^2) | 0 |
6 | 5 | 4 | 2 | 0 |
7 | 6 | 5 | 3 | 0 |
8 | 7 | 6 | 4 | 0 |
9 | 8 | 7 | 5 | 1(1+2^3) |
看得出来,横行表示的是2的幂数,也就是fa[x][j]中的j。
将j和x颠倒似乎能优化。
那么每次dfs一个点的时候,我们先记录fa[x][0]为该点的直接父亲,然后将它初始化:
for (register int i=0;fa[x][i];++i) fa[x][i+1]=fa[fa[x][i]][i];
若要求两点的LCA,首先要记录它们的深度,然后倍增:
inline int LCA(int u,int v) { if(dep[u]<dep[v])swap(u,v); for(R int i=0;i<=16;i++) if((dep[u]-dep[v])&(1<<i))u=fa[u][i]; if(u==v)return u; for(R int i=16;i>=0;i--) if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i]; return fa[u][0]; }
毕竟,指数爆炸可是很快的。这是一个清清楚楚的logn的复杂度。