线段树

本文深入探讨了线段树在区间修改与查询中的高效应用,以及单调栈在解决最大矩形面积问题中的作用。同时,介绍了单调队列在求解滑动窗口极值问题上的巧妙运用。通过实例展示了这些数据结构和算法如何在实际问题中实现高效解决方案。
摘要由CSDN通过智能技术生成

线段树:

线段树或前缀树都可以来求区间和
线段树:log(n),适用于修改
使用懒标记用作修改

题目描述
​ 给定一个 n 位数组和两种操作:

​ 操作1:数组中某个区间的所有数字加上一个值

​ 操作2:查询数组中某个区间的所有数字之和

输入
​ 第一行输入两个整数 n,m(1≤n≤10000,3≤m≤100000)​,分别代表数组大小和操作数。

​ 第二行包含 n 个整数,代表数组中相应的数字,数字大小不会超过 int 表示范围。

​ 接下来 m 行,每行三个或四个整数 a,b,c,d(a∈[1,2])
当 a=1​ 时,接下来输入 b,c,d​,代表将数组 [b,c]​区间内的数字加上 d,(1≤b,c≤n, d is int)​,当 b>c 时,不做任何操作。
当 a=2 时,接下来输入 b,c,输入代表询问 [b,c] 区间内的和值 ( 1≤b,c≤n),当 b>c 时,输出 0。
输出
​ 对于每个 a=2 的操作,输出查询区间内数字的和值,答案不会超过64位整型(long long)的表示范围。

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node{
    int l,r,cnt;
    long long sum,lazy;
};
node  tree[40005];
int n,m;
long long num[10005];
void up_sum(int now){
    tree[now].sum=tree[now*2].sum+tree[now*2+1].sum;
}
void down_lazy(int now){
    if(tree[now].lazy!=0){
        tree[now*2].lazy+=tree[now].lazy;
        tree[now*2].sum+=tree[now*2].cnt*tree[now].lazy;
        tree[now*2+1].lazy+=tree[now].lazy;
        tree[now*2+1].sum+=tree[now*2+1].cnt*tree[now].lazy;
        tree[now].lazy=0;
    }
}
void built_tree(int now,int l,int r){
    tree[now].l=l;
    tree[now].r=r;
    tree[now].cnt=r-l+1;
    tree[now].lazy=0;
    if(l==r){
        tree[now].sum=num[l];
        return ;
    }
    int mid=(l+r)/2;
    built_tree(now*2,l,mid);
    built_tree(now*2+1,mid+1,r);
    up_sum(now);
}
void modify(int now,int l,int r,int v){
    if(tree[now].l>=l&&tree[now].r<=r){
        tree[now].sum+=tree[now].cnt*v;
        tree[now].lazy+=v;
        return ;
    }
    down_lazy(now);
    int mid=(tree[now].l+tree[now].r)/2;
    if(mid>=l)modify(now*2,l,r,v);
    if(mid<r)modify(now*2+1,l,r,v);
    up_sum(now);
}
long long query(int now,int l,int r){
    if(tree[now].l>=l&&tree[now].r<=r){
        return tree[now].sum;
    }
    down_lazy(now);
    int mid=(tree[now].l+tree[now].r)/2;
    long long t=0;
    if(mid>=l)t+=query(now*2,l,r);
    if(mid<r)t+=query(now*2+1,l,r);
    return t;
}
int main(){
    cin>>n>>m;
    for(int i=0;i<=n;i++){
       cin>>num[i];
    }
    built_tree(1,1,n);
    for(int i=0;i<m;i++){
        int t,a,b,c;
        cin>>t;
        if(t==1){
            cin>>a>>b>>c;
            modify(1,a,b,c);
        }else{
            cin>>a>>b;
            cout<<query(1,a,b)<<endl;
        }
    }
    return 0;
}

单调栈(有序栈):

适合向前或向后大于小于某个数的问题

leetcode84. 柱状图中最大的矩形
class Solution {
public:
   struct node{
       int ind,h;
   };
    int largestRectangleArea(vector<int>& heights) {
      stack<node>sta;
      sta.push((node){-1,-1});
      int ans=0;
      for(int i=0;i<heights.size();i++){
          while(sta.size()!=1&&sta.top().h>heights[i]){
                 node temp=sta.top();
                 sta.pop();
                 ans=max(ans,temp.h*(i-sta.top().ind-1));
          }
          sta.push(node{i,heights[i]});
      }
      while(sta.size()!=1){
           node temp=sta.top();
           sta.pop();
           ans=max(ans,temp.h*((int)heights.size()-1-sta.top().ind));
      }
      return ans;
    }
};

行人看楼问题:

从前往后求个单调递减栈
再从后往前求个单调递减栈

单调队列:

题目描述
​ 给出一个长度为 N 的数组,一个长为 K 的滑动窗口从最左移动到最右,每次窗口移动,如下图:

找出窗口在各个位置时的极大值和极小值。

输入
​第一行两个数 N,K。
第二行有 N 个数,表示数组中的元素。
输出
​ 输出两行,第一行为窗口在各个位置时的极小值,第二行为窗口在各个位置时的极大值。
样例输入
8 3
1 3 -1 -3 5 3 6 7
样例输出
-1 -3 -3 -3 3 3
3 3 5 5 6 7

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node{
    int ind,val;
};
int n,k,num[300005],a1[300005],a2[300005];
int main(){
    cin>>n>>k;
    deque<node>mmin,mmax;
    for(int i=0;i<=n;i++){
       cin>>num[i];
       while(!mmin.empty()&&mmin.back().val>num[i]){
           mmin.pop_back();
       }
       mmin.push_back((node){i,num[i]});
        if(mmin.front().ind+k<=i){
        mmin.pop_front();
        }    
       while(!mmax.empty()&&mmax.back().val<num[i]){
           mmax.pop_back();
        }
       mmax.push_back((node){i,num[i]});
       if(mmax.front().ind+k<=i){
        mmax.pop_front();
        }   
        if(i>=k){
            a1[i]=mmin.front().val;
            a2[i]=mmax.front().val;
        } 
    }
    for(int i=k;i<=n;i++){
        if(i!=k)cout<<" ";
        cout<<a1[i];
    }
    cout<<endl;
    for(int i=k;i<=n;i++){
        if(i!=k)cout<<" ";
        cout<<a2[i];
    }
    cout<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值