动态最值

问题 B: 动态最值

时间限制: 5 Sec 内存限制: 128 MB

题目描述

有一个包含n个元素的数组,要求实现以下操作:
DELETE k:删除位置k上的数。右边的数往左移一个位置。
QUERY i j:查询位置i~j上所有数的最小值和最大值。
例如有10个元素:

QUERY 2 8的结果为2 9。依次执行DELETE 3和DELETE 6(注意这时删除的是原始数组的元素7)后数组变为:

QUERY 2 8的结果为1 7。

输入

第一行包含两个数n, m,表示原始数组的元素个数和操作的个数。第二行包括n个数,表示原始数组。以下m行,每行格式为1 k或者2 i j,其中第一个数为1表示删除操作,为2表示询问操作。

输出

对每个询问操作输出一行,包括两个数,表示该范围内的最小值和最大值。

样例输入

10 4
1 5 2 6 7 4 9 3 1 5
2 2 8
1 3
1 6
2 2 8

样例输出

2 9
1 7

提示

100%的数据满足1<=n, m<=10^6, 1<=m<=10^6
对于所有的数据,数组中的元素绝对值均不超过109

单点更新,区间查询,适合用线段树来解决。其中注意删除操作的实现。查询区间最大最小值时,注意右子树的标号控制。

#include <bits/stdc++.h>
#define L(u) (u<<1)
#define R(u) (u<<1|1)
using namespace std;
const int mx=1000001;
struct Tree {
    int lch,rch,Min,Max,num;
} tree[mx<<2];//Min:区间最小值 Max:区间最大值 num:区间内尚存的,即未被删除的元素数目
int d[mx],m,n,l,r,ans1,ans2;
char s;
void read(int &k) {
    int f=1;
    char c=getchar();
    while ((c<48||c>57)&&c!='-') c=getchar();
    if (c=='-') f=-1,c=getchar();
    k = 0;
    while (48<=c&&c<=57) k=k*10+c-48,c=getchar();
    k=f*k;
}
void Pushup(int u) {
    tree[u].Max=max(tree[L(u)].Max,tree[R(u)].Max);
    tree[u].Min=min(tree[L(u)].Min,tree[R(u)].Min);
    tree[u].num=tree[L(u)].num+tree[R(u)].num;
}
void Build(int u,int l,int r) {//建树。根节点编号是1
    tree[u].lch=l;
    tree[u].rch=r;
    if (l==r) {//叶节点
        tree[u].Max=tree[u].Min=d[l];
        tree[u].num=1;
        return ;
    }
    int mid=(l+r)>>1;
    Build(L(u),l,mid);//递归,建左子树
    Build(R(u),mid+1,r);//递归,建右子树
    Pushup(u);//建完两棵子树,向上更新一下
}
void Delete(int u,int num) {//删除节点的操作实现。这个num是待删元素的标号
    if((tree[u].lch==tree[u].rch)) {//叶节点。删的就是它
        tree[u].num--;//没有元素了
        tree[u].Min=2147483647;
        tree[u].Max=-2147483648;
        return;
    }
    int n1=tree[L(u)].num;
    if(num<=n1) Delete(L(u),num);//标号≤左子树所含元素个数,则待删元素在左子树上,递归找它
    else Delete(R(u),num-n1);//不是左子树就是右子树啦。注意这里往右子树递归的时候,标号减去左子树所含元素数目
    Pushup(u);//少了元素了,需要更新
}
void Query(int u,int l,int r) {
    if (l==1&&tree[u].num==r) {
        ans1=min(ans1,tree[u].Min);
        ans2=max(ans2,tree[u].Max);
        return ;
    }
    int n1=tree[L(u)].num,n2=tree[R(u)].num;
    if (l<=n1&&r>n1) {//待查询区间跨了左右两棵子树
        Query(L(u),l,n1);
        Query(R(u),1,r-n1);
    } else if (l<=n1&&r<=n1) Query(L(u),l,r);//左子树
    else if (l>n1&&r<=n1+n2) Query(R(u),l-n1,r-n1);//右子树
}
int main() {
    read(n);
    read(m);
    for (int i=1; i<=n; ++i) read(d[i]);
    for (int i=1; i<=4*n; ++i) tree[i].Min=2147483647,tree[i].Max=-2147483648;//Too young. 写成INF、-INF多好
    Build(1,1,n);
    while (m--) {
        scanf(" %c",&s);
        if (s=='1') {
            scanf("%d",&l);
            Delete(1,l);
        } else {
            scanf("%d%d",&l,&r);
            ans1=2147483647,ans2=-2147483648;
            Query(1,l,r);
            printf("%d %d\n",ans1,ans2);
        }
    }
    return 0;
}

/**************************************************************
    Problem: 4124
    User: WC006
    Language: C++
    Result: 正确
    Time:1216 ms
    Memory:83732 kb
****************************************************************/

猜你喜欢

转载自blog.csdn.net/ACM2017/article/details/79616223