RMQ问题 ST表 & 树状数组 线段树笔记

静态区间最值问题

推荐使用ST表倍增思想
此类题目一般查询数量巨大,所以O(log n)不足以AC,需将查询复杂度优化为O(1)

ST表模板如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
int dpMin[maxn][20],dpMax[maxn][20];
int a[maxn];
int n,q;
int mn[1000006];
void RMQ()
{
    for(int i=1;i<=n;i++)
        dpMin[i][0] = dpMax[i][0] = a[i];
    
    for(int j=1;(1<<j)<=n;j++) //枚举区间长度
        for(int i=1;i+(1<<j)-1<=n;i++){ //枚举左端点
            dpMin[i][j]=min(dpMin[i][j-1],dpMin[i+(1<<(j-1))][j-1]);
            dpMax[i][j]=max(dpMax[i][j-1],dpMax[i+(1<<(j-1))][j-1]);
        }	
	//for(int len=1;len<=n;++len){
     //   int k=0;
    //    while((1<<(k+1))<=len)
     //       k++;
     //   mn[len]=k; //预处理 2的幂次
    }
}
int getMin(int L,int R)
{
	int k=log(r-l+1)/log(2.0);
    //int k=mn[R-L+1]; log写法无需预处理 2的幂次
    return min(dpMin[L][k],dpMin[R-(1<<k)+1][k]);
}
int getMax(int L,int R)
{
	int k=log(r-l+1)/log(2.0);
   // int k=mn[R-L+1];  log写法无需预处理 2的幂次
    return max(dpMax[L][k],dpMax[R-(1<<k)+1][k]);
}
int main()
{
    cin>>n>>q;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    RMQ();
    while(q--)
    {
        int l,r;scanf("%d%d",&l,&r);
        printf("最大值:%d\n",getMax(l,r));
        printf("最小值:%d\n",getMin(l,r));
    }
    return 0;
}

注意: 1<<n 等于 2^n


树状数组&线段树

用来解决区间和,最值,等问题(动态)
查询复杂度 O(log n)

图片来自百度百科图片来自百度百科,如有侵权,联系删除
特性: 如图12号块覆盖了 9 10 11 12 四个数
12的二进制表示为 1100

  • 末尾1前面有2个0 所以12覆盖的大小为 2^2 = 4
  • 区间和为去掉末尾二进制1后转10进制
    • a[1]-a[12] == a[12]+a[8]
    • 1100 == 12
    • 1000 == 8
    • END:查询为减去lowbit

去掉末尾1的操作是位运算 常写作lowbit

int lowbit(int x){
	return x&(-x);
}
  • 区间和
    • 【5,9】的和可以 a[9] - a[4]
  • 修改
    • 令a[5] +1 ,5的二进制为101 结果5+(5&(-5)) = 6,所以a[6]也加1 以此类推,范围最大为原数组长度。
    • END:修改为增加lowbit
void add(int x, int y){ //给x增加 y
	while(x<=n){
		a[x]+=y;
		x+=lowbit(x);
	}
}
//建树也是通过此函数,树状数组初值为0,之后将数值逐一填入。

线段树

也叫满二叉树,树的节点存放区间和

规律
left_node = 2 * node + 1
right_node = 2 * node +2

递归建树:
void build_tree(int a[],int tree[],int node,int L,int R){
	if(L==R){
		tree[node] = a[L];
	}
	else{
		int mid = L+R/2;
		int left_node = 2 * node + 1;
		int right_node = 2 * node + 2;
	
		build_tree( a , tree , left_node ,  L, mid);
		build_tree( a , tree , right_node, mid+1, R);
		tree[node] = tree[left_node]+tree[right_node];
	}
}
主函数内 build_tree( a , tree , 0 , 0 , len-1);

猜你喜欢

转载自blog.csdn.net/zhimeng_LQ/article/details/106079107
今日推荐