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;
}