RMQ与LCA入门&&ST算法的运用

mac2022-06-30  118

RMQ

RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。——百度百科

举个例子:在1 0 4 99 8 5这一串数中求第2个数到第5个数的最小值。

有什么办法?

最简单的莫过于循环一次,时间为O(N).但如果有许多个询问呢?

这时就要用到ST算法了。利用动规预处理出每一段的最值,对于每个询问,只要O(1)的时间便能得出答案。

动规如下:f[i][j]表示从第i个位置开始的2^j个数中的最小值。转移方程如下:

f[i][j]=min(f[i][j-1],f[i+2^(j-1)][j-1])

这样,对于每个查询x,y(x<y)(在第x个位置到第y个位置的最值),答案就是

min(f[x][j],f[y-2^(j)+1][j])(其中j是log2(y-x+1)) ∵[x,x+2^j]与[y-2^(j)]都是[x,y]的子区间且[x,x+2^j]U[y-2^(j)]=[x,y]。

至此RMQ问题就解决了,时间复杂度为O(nlogn)+O(1)*q(其中q为询问数量)

当然还有其他的方法这里就不讨论了

LCA

最近公共祖先(Least Common Ancestors)LCA简介:对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。另一种理解方式是把T理解为一个无向无环图,而LCA(T,u,v)即u到v的最短路上深度最小的点。——百度百科

求LCA的其中一种算法便是转换成RMQ,利用ST算法求解。具体做法如下: 将这棵树用深度优先遍历,每次遍历一个点(包括回溯)都添加进数组里面。找到所询问的点第一次在其出现的位置,两个位置所夹的点中深度最小的即为所求。

如图求3与6的LCA。首先利用深度优先遍历得到一个数列:1,2,3,2,4,5,6

找到3第一次出现以及6第一次出现的位置。所夹数列即为3,2,4,5,6。其中深度最小的点就是他们的LCA,也就是2.

 

 

 

 

 

例题

(这里就直接贴代码了,详细题解可见这里)

1、hdu2586:

View Code 1 #include <iostream> 2 #include <cmath> 3 #include <algorithm> 4 using namespace std; 5 struct node{ 6 int link,dt; 7 node *next; 8 }*son[40009],ES[100009]; 9 int n,m,EC,t,f[100009][20],bit[20],lg, 10 fir[40009],enter[100009],dis[40009],deep[40009],lca[100009][20]; 11 void addedge(int x,int y,int z) 12 { 13 ES[++EC].next=son[x]; 14 son[x]=ES+EC; 15 son[x]->link=y; 16 son[x]->dt=z; 17 } 18 void tr(int fa,int v) 19 { 20 enter[++t]=v; 21 if (!fir[v]) fir[v]=t; 22 for (node *tmp=son[v];tmp;tmp=tmp->next) 23 if (tmp->link!=fa){ 24 int u=tmp->link; 25 deep[u]=deep[v]+1; 26 dis[u]=dis[v]+tmp->dt; 27 tr(v,u); 28 enter[++t]=v; 29 } 30 } 31 void dp() 32 { 33 for (int i=1;i<=2*n-1;++i) f[i][0]=deep[enter[i]],lca[i][0]=i; 34 for (int j=1;j<=lg;++j) 35 for (int i=1;i<=2*n-bit[j]+1;++i) 36 if (f[i][j-1]<f[i+bit[j-1]][j-1]) f[i][j]=f[i][j-1],lca[i][j]=lca[i][j-1]; 37 else f[i][j]=f[i+bit[j-1]][j-1],lca[i][j]=lca[i+bit[j-1]][j-1]; 38 } 39 int main() 40 { 41 #ifndef ONLINE_JUDGE 42 freopen("hdu2586.in","r",stdin);freopen("hdu2568.out","w",stdout); 43 #endif 44 bit[0]=1; 45 for (int i=1;i<=19;++i) bit[i]=bit[i-1]*2; 46 ios::sync_with_stdio(false); 47 int T; 48 cin>>T; 49 for (int AC=T;AC;--AC){ 50 EC=0; 51 t=0; 52 deep[1]=1; 53 memset(f,0,sizeof(f)); 54 memset(fir,0,sizeof(fir)); 55 memset(son,0,sizeof(son)); 56 cin>>n>>m; 57 lg=floor(log2(2*n-1)); 58 int x,y,z,k; 59 for (int i=n-1;i;--i){ 60 cin>>x>>y>>z; 61 addedge(x,y,z); 62 addedge(y,x,z); 63 } 64 tr(0,1); 65 dp(); 66 for (int i=m;i;--i){ 67 cin>>x>>y; 68 int ans=dis[x]+dis[y]; 69 x=fir[x];y=fir[y]; 70 if (x>y) swap(x,y); 71 lg=floor(log2(y-x+1)); 72 if (f[x][lg]<f[y-bit[lg]+1][lg]) k=lca[x][lg]; 73 else k=lca[y-bit[lg]+1][lg]; 74 cout<<ans-2*dis[enter[k]]<<endl; 75 } 76 } 77 #ifndef ONLINE_JUDGE 78 fclose(stdin);fclose(stdout); 79 #endif 80 }

2、poj3264:

#include <cstdio> #include <cmath> #include <algorithm> #include <iostream> using namespace std; int n,q,h[50009],Fmin[50009][17],Fmax[50009][17],lg,bit[17]; int main() { #ifndef ONLINE_JUDGE freopen("lineup.in","r",stdin);freopen("lineup.out","w",stdout); #endif scanf("%d%d",&n,&q); bit[0]=1; for (int i=1;i<=16;++i) bit[i]=2*bit[i-1]; for (int i=1;i<=n;++i){ scanf("%d",&h[i]); Fmin[i][0]=Fmax[i][0]=h[i]; } lg=floor(log10((double)n)/log10((double)2)); for (int j=1;j<=lg;++j) for (int i=1;i<=n+1-bit[j];++i){ Fmin[i][j]=min(Fmin[i][j-1],Fmin[i+bit[j-1]][j-1]); Fmax[i][j]=max(Fmax[i][j-1],Fmax[i+bit[j-1]][j-1]); } int x,y; for (int i=1;i<=q;++i){ scanf("%d%d",&x,&y); if (x>y) swap(x,y); lg=floor(log10((double)(y-x+1))/log10((double)2)); printf("%d\n",max(Fmax[x][lg],Fmax[y-bit[lg]+1][lg])- min(Fmin[x][lg],Fmin[y-bit[lg]+1][lg])); } #ifndef ONLINE_JUDGE fclose(stdin);fclose(stdout); #endif }

转载于:https://www.cnblogs.com/lazycal/archive/2012/08/11/2633486.html

相关资源:ACM国际大学生程序设计竞赛:算法与实现(带书签)
最新回复(0)