RMQ 与 LCA

RMQ (range maximum/ minimum query)

首先介绍一下 ST表也就是(sparse Table算法),该算法基于倍增思想(倍增好像跟dp有点关系),该算法需要用O(nlogn)的时间来预处理,然后每次查询的时候就可以达到O(1)的时间复杂度,也就是说它不支持在线修改。

初始化 最小值

for(int j=1;(1<<j)<=n;j++)
	{
		for(int i=1;i+(1<<j)-1<=n;i++)
		{
			dp_min[i][j]=min(dp_min[i][j-1],dp_min[i+(1<<(j-1))][j-1]);
		}	
	}

同理初始化 最大值

	for(int j=1;(1<<j)<=n;j++)
	{
		for(int i=1;i+(1<<j)-1<=n;i++)
		{
			dp_max[i][j]=max(dp_max[i][j-1],dp_max[i+(1<<(j-1))][j-1]);
		}
	}

O(1) 查询

	int Max,Min;
	for(int i=0;i<m;i++)
	{
		cin>>x>>y;
		int k=log2(y-x+1);
		Max=max(dp_max[x][k],dp_max[y-(1<<k)+1][k]);
		Min=min(dp_min[x][k],dp_min[y-(1<<k)+1][k]);
		cout<<Max-Min<<endl;
	}

LCA(有根树两个节点的一个祖先结点且满足该结点的深度最大)

倍增法求LCA

注释比较齐全

#include<iostream>
#include<cstdio>
#include<stack>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=1000000+10;
const int maxm=1000000+10;
struct Edge
{
	int before;
	int to;
}e[maxm<<2];
int n,m,s,k,head[maxn],dep[maxn],fa[maxn][25];
int lg[maxn];// 为了减小常数 
void add(int u,int v)
{
	e[k].before=head[u];
	e[k].to=v;
	head[u]=k++;
}
void DFS(int u,int father)
{
	dep[u]=dep[father]+1; // 当前结点深度等于父节点深度 + 1 
	fa[u][0]=father; //  从当前结点向上跳 2^0次,到达父亲结点 
	for(int i=1;i<=lg[dep[u]];i++)
	{
		fa[u][i]=fa[fa[u][i-1]][i-1]; // 得到当前结点向上跳 2^i 次之后到达的点  
	}
	for(int i=head[u];i!=-1;i=e[i].before)
	{
		int v=e[i].to;
		if(v==father)
		continue;
		DFS(v,u); // 继续搜索 
	}
}
int LCA(int x,int y)
{
	if(dep[x]<dep[y]) 
	{
		swap(x,y);// 从深度更大的那个点向上跳跃 
	}
	while(dep[x]>dep[y])   
	{
		x=fa[x][lg[dep[x]-dep[y]]-1]; // 调整到同一层 
  	}
	if(x==y)
	return x;//  两个点在同一层且是同一个点 那么当前点就是 LCA 了 
	for(int i=lg[dep[x]]-1;i>=0;i--) // 先从大的开始  跳得多那么一开始得到的结点肯定是一样的 
	{								 // 如果跳 某2^i后不是同一个点了 那么跳2^(i+1)后肯定是  
		if(fa[x][i]!=fa[y][i])		// 因为是从大到小开始枚举的
		{
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0]; // 再向上跳一个 
}
int main()
{
	#ifdef ONLINE_JUDGE
	#else
	freopen("in.txt","r",stdin);
	#endif
	int x,y;
	memset(head,-1,sizeof head);
	scanf("%d %d %d",&n,&m,&s);
	for(int i=0;i<n-1;i++)
	{
		scanf("%d %d",&x,&y);
		add(x,y);// 为什么要加两条边 ? 
		add(y,x); // 因为在俺不知道谁是根结点啊 
	}
	for(int i=1;i<=n+10;i++)
	{
		lg[i]=lg[i-1]+(1<<lg[i-1]==i);// 提前算法减小常数 
	}
	dep[s]=0;	// 根节点深度为 0  
	DFS(s,-1);	// 从根节点开始深搜 
	for(int i=0;i<m;i++)
	{
		scanf("%d %d",&x,&y);
		printf("%d\n",LCA(x,y));
	}
	return 0;
}
发布了51 篇原创文章 · 获赞 21 · 访问量 3078

猜你喜欢

转载自blog.csdn.net/qq_44115065/article/details/103333877