静态区间最值问题
推荐使用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);