ST算法与LCA

来源:互联网 发布:postek标签打印机软件 编辑:程序博客网 时间:2024/04/29 06:42

上一次我们讲到了倍增算法。倍增算法是O(nlogn)-O(logn)的复杂度,这里我们介绍另外一种方法,可以达到O(nlogn)-O(1)的复杂度。事实上,在经过+-1RMQ的处理,可以做到O(n)-O(1)的复杂度。

先举个例子:


我们设L[i]为第i次访问到的节点编号,d[i]为第i次访问到的节点的深度,fir[i]为节点编号为i在L中第一次出现的位置。则:

L: 1,2,5,2,6,2,1,3,1,4,7,8,7,4,1

D:1,2,3,2,3,2,1,2,1,2,3,4,3,2,1

fir:1,2,8,10,3,5,11,12

由于每条边都出现了两次,则可以得出,L的长度是2n-1。

如果现在要求6,7的LCA,则:

L: 1,2,5,2,6,2,1,3,1,4,7,8,7,4,1

D:1,2,3,2,3,2,1,2,1,2,3,4,3,2,1

可以发现,红色区域的最小值的位置所对应的L数组中的数即是LCA了。

则:LCA(T,x,y) = L[RMQ(D,fir[x],fir[y])]

这样做的时间复杂度就是O(nlogn)-O(1)。

只不过RMQ记录的不是最小值,而是最小值所在的位置。

如果还有些不懂的,请看程序:

#include <cstdio>#include <iostream>#include <algorithm>#include <cstring>#include <string>#include <vector>#include <queue>#include <stack>#include <map>#include <set>#include <cmath>using namespace std;#define Maxn 100010vector<int> adj[Maxn];int l[Maxn<<1],d[Maxn<<1];int fir[Maxn];int f[Maxn<<1][22];int tot;int n,q;void AddEdge(int x,int y){adj[x].push_back(y);adj[y].push_back(x);}void dfs(int u,int pre,int depth){++tot;l[tot] = u; d[tot] = depth; fir[u] = tot;int len = adj[u].size();for(int i=0;i<len;i++){int v = adj[u][i];if(v != pre){dfs(v,u,depth+1);++tot;l[tot] = u; d[tot] = depth;}}}void Init_RMQ(){for(int i=1;i<=tot;i++) f[i][0] = i;for(int j=1;(1<<j)<=tot;j++)for(int i=1;i+(1<<j)-1 <= tot;i++){int A = f[i][j-1],B = f[i+(1<<(j-1))][j-1];f[i][j] = d[A] < d[B]?A:B;}}int Ask_RMQ(int x,int y){if(x > y) swap(x,y);int k = log(y-x+1.0) / log(2.0);int A = f[x][k],B = f[y-(1<<k)+1][k];return d[A] < d[B]?A:B;}int LCA(int x,int y){x = fir[x]; y = fir[y];if(x>y) swap(x,y);return l[Ask_RMQ(x,y)];}int main(){scanf("%d",&n);int x,y;for(int i=1;i<n;i++){scanf("%d%d",&x,&y);AddEdge(x,y);}dfs(1,0,1);Init_RMQ();scanf("%d",&q);while(q--){scanf("%d%d",&x,&y);printf("%d\n",LCA(x,y));}return 0;}


原创粉丝点击