【线段树】动态最值

  • 题目描述
    有一个包含n个元素的数组,要求实现以下操作:
    DELETE k:删除位置k上的数。右边的数往左移一个位置。
    QUERY i j:查询位置i~j上所有数的最小值和最大值。
    例如有10个元素:
    这里写图片描述
    QUERY 2 8的结果为2 9。依次执行DELETE 3和DELETE 6(注意这时删除的是原始数组的元素7)后数组变为:
    这里写图片描述
  • 输入
    输入文件minmax.in第一行包含两个数n, m,表示原始数组的元素个数和操作的个数。第二行包括n个数,表示原始数组。以下m行,每行格式为1 k或者2 i j,其中第一个数为1表示删除操作,为2表示询问操作。
  • 输出
    输出文件minmax.out对每个询问操作输出一行,包括两个数,表示该范围内的最小值和最大值。
  • 样例输入
    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
  • 提示
    50%的数据满足1<=n, m<=10^4,删除操作不超过100个
    100%的数据满足1<=n, m<=10^6, 1<=m<=10^6
    对于所有的数据,数组中的元素绝对值均不超过10^9
  • 题目大意
    一段区间可支持删除某节点并且编号改变(类似数组中删除元素)并且支持查询区间最大值最小值。

乍一看这题是一个数组和链表的结合体,想到用块状链表,但是计算一下时间代价是 109 会超时(这里我同学写了,果然超时,详情见传送门),然后乍一瞅区间最值,也许是线段树,但是又不会删除,就懵逼了。。
后来才知道,原来线段树也可以支持删除节点,只需要在原来的存储方式上变化一下就好了。
做法:把原来的几点存区间左右边界变成类似平衡树那样的存节点size,然后查询的时候比较左右节点的size和当前要查询的点号就行:
查询分三种情况
1. 查询区间左端点比当前左节点的size小,右端点比做节点size大,证明区间在左右子树中,分别查询。
2. 查询区间左端点比左子节点size小,右端点比左子节点size小,查左子树。
3. 查询区间左端点比左子节点size大,右子节点也比它大,查右子树。
删除分两种
1. 删除number比左size小,递归左;
2. 删除number比左大,递归右;
然后就没了。。。。线段树好流弊啊。。。
代码

#include <iostream>
#include <cstdio>
#include <cstring>
#define INF 0x7fffffff
#define MAXN 1000100
using namespace std;
int in[MAXN];
int ansmin=INF,ansmax=-INF;
struct Segment_tree{
    struct node{
        int l,r;
        int num;
        int minn;
        int maxx;
    };
    node tree[MAXN*4];
    void pre(int n){
        memset(tree,0,sizeof tree);
        for(int i=0;i<=4*n;i++)
        tree[i].minn=INF,tree[i].maxx=-INF;
    }
    void build(int left,int right,int root){
        tree[root].l=left;
        tree[root].r=right;
        if(left==right){
            tree[root].maxx=tree[root].minn=in[left];
            tree[root].num=1;
            return;
        }
        int mid=(left+right)>>1;
        build(left,mid,root*2);
        build(mid+1,right,root*2+1);
        tree[root].num=tree[root*2].num+tree[root*2+1].num;
        tree[root].minn=min(tree[root*2].minn,tree[root*2+1].minn);
        tree[root].maxx=max(tree[root*2].maxx,tree[root*2+1].maxx);
    }
    void query(int left,int right,int root){
        if(left==1&&(right==tree[root].num))
        {
            ansmin=min(ansmin,tree[root].minn);
            ansmax=max(ansmax,tree[root].maxx);
            return;
        }
        int n1=tree[root*2].num;
        int n2=tree[root*2+1].num;
        if(left<=n1&&right>n1){
            query(left,n1,root*2);query(1,right-n1,root*2+1);
        }
        else if(left<=n1&&right<=n1) query(left,right,root*2);
        else if(left>n1&&right<=n1+n2) query(left-n1,right-n1,root*2+1);
    }
    void del(int num,int root){
        if((tree[root].l==tree[root].r)){
            tree[root].num=tree[root].num-1;
            tree[root].minn=INF,tree[root].maxx=-INF;
            return;
        }
        int n1=tree[root*2].num;
        int n2=tree[root*2+1].num;
        if(num<=n1) del(num,root*2);
        else del(num-n1,root*2+1);
        tree[root].num=tree[root*2].num+tree[root*2+1].num;
        tree[root].minn=min(tree[root*2].minn,tree[root*2+1].minn);
        tree[root].maxx=max(tree[root*2].maxx,tree[root*2+1].maxx);
    }
}S;
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    S.pre(n);
    for(int i=1;i<=n;i++)
        scanf("%d",&in[i]);
    S.build(1,n,1);
    for(int i=1;i<=m;i++){
        int a;
        scanf("%d",&a);
        if(a==1){
            int temp;
            scanf("%d",&temp);
            S.del(temp,1);
        }
        else if(a==2){
            int tep1,tep2;
            ansmin=INF,ansmax=-INF;
            scanf("%d%d",&tep1,&tep2);
            S.query(tep1,tep2,1);
            printf("%d %d\n",ansmin,ansmax);
        }
    }
    return 0; 
}

时间
正在测试 minmax.0(minmax.0.in)… 正确 0.082秒
正在测试 minmax.1(minmax.1.in)… 正确 0.088秒
正在测试 minmax.2(minmax.2.in)… 正确 0.087秒
正在测试 minmax.3(minmax.3.in)… 正确 0.079秒
正在测试 minmax.4(minmax.4.in)… 正确 0.093秒
正在测试 minmax.5(minmax.5.in)… 正确 0.630秒
正在测试 minmax.6(minmax.6.in)… 正确 0.888秒
正在测试 minmax.7(minmax.7.in)… 正确 1.417秒
正在测试 minmax.8(minmax.8.in)… 正确 1.172秒
正在测试 minmax.9(minmax.9.in)… 正确 2.121秒

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个用线段树算区间最值的示例代码,以查询区间最小值为例: ```cpp #include <cstdio> #include <algorithm> using namespace std; const int MAXN = 100005; int n, m; int a[MAXN]; struct Node { int l, r, minn; } tr[MAXN*4]; // 建立线段树 void build(int u, int l, int r) { tr[u].l = l, tr[u].r = r; if (l == r) { // 叶子节点 tr[u].minn = a[l]; return; } int mid = (l + r) >> 1; build(u<<1, l, mid); build(u<<1|1, mid+1, r); tr[u].minn = min(tr[u<<1].minn, tr[u<<1|1].minn); } // 查询区间最小值 int query(int u, int ql, int qr) { if (ql <= tr[u].l && tr[u].r <= qr) { // 完全包含 return tr[u].minn; } int mid = (tr[u].l + tr[u].r) >> 1; int res = 0x3f3f3f3f; // 初始化为一个极大值 if (ql <= mid) res = min(res, query(u<<1, ql, qr)); // 左子区间有交集 if (qr > mid) res = min(res, query(u<<1|1, ql, qr)); // 右子区间有交集 return res; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); } build(1, 1, n); // 建立线段树 while (m--) { int l, r; scanf("%d%d", &l, &r); printf("%d\n", query(1, l, r)); } return 0; } ``` 这段代码中,建立线段树的过程采用递归方式实现,查询区间最小值的过程也采用递归方式实现。对于每个节点,维护该节点对应区间的最小值。查询区间最小值的过程,采用线段树的递归查询方式,将查询区间划分成若干个离散的单元,并依次查询每个单元,最终得到查询区间的最小值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值