(单调栈+线段树)2478 小b接水

2478 小b接水

  1. 2 秒
  2.  
  3. 262,144 KB
  4.  
  5. 20 分
  6.  
  7. 3 级题

小b将n个宽度相同的积木顺序摆在一起,如下图所示。

现在她告诉你每个积木的高度(可能为0)。

她想知道如果她从高处倒下一杯水,最多有多少单位的水能被积木接住?

假设每个积木的宽度都为1。

上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,最多可以接 6 个单位的水(蓝色部分表示水)。 

 收起

输入

第一行一个正整数n,其中0<n≤50000;
第二行n个数表示从左到右每个积木的高度,以空格隔开,每个数不超过10000。

输出

一个数,表示最多可以接的水量

输入样例

12
0 1 0 2 1 0 1 3 2 1 2 1

输出样例

6

题解:分类讨论: 1>   右边存在比当前值大的            2  1   3

                              2>   右边不存在比当前值大的        5   1   2    3   1     3    1     2

                                     5   1   2    3   1     3       算一个区间

                                     3    1     2      算一个区间

           很显然,第一种用单调栈就行;第二种用map记录一下右边第一次出现某个值的位置(移动时用),用线段树求出当前值                           右边最大值,显然,这么组合是最优的。

#include<set>
#include<map>
#include<list>
#include<queue>
#include<stack>
#include<math.h>
#include<vector>
#include<bitset>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#define eps (1e-8)
#define MAX 0x3f3f3f3f
#define u_max 1844674407370955161
#define l_max 9223372036854775807
#define i_max 2147483647
#define re register
#define pushup() tree[rt]=max(tree[rt<<1],tree[rt<<1|1])
#define nth(k,n) nth_element(a,a+k,a+n);  // 将 第K大的放在k位
#define ko() for(int i=2;i<=n;i++) s=(s+k)%i // 约瑟夫
#define ok() v.erase(unique(v.begin(),v.end()),v.end()) // 排序,离散化
using namespace std;

inline int read(){
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' & c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}

typedef long long ll;
const double pi = atan(1.)*4.;
const int M=1e3+5;
const int N=5e5+5;
int a[N],l[N],r[N],tree[N];
stack<int>s1,s2;
void sett(int l,int r,int rt){
    if(l==r){
        tree[rt]=a[l];
        return ;
    }
    int mid=l+r>>1;
    sett(l,mid,rt<<1);
    sett(mid+1,r,rt<<1|1);
    pushup();
}

int findd(int x,int y,int l,int r,int rt){
    if(x<=l&&r<=y)
        return tree[rt];
    int mid=l+r>>1;
    int ans=0;
    if(x<=mid)
        ans=max(ans,findd(x,y,l,mid,rt<<1));
    if(y>mid)
        ans=max(ans,findd(x,y,mid+1,r,rt<<1|1));
    return ans;
}
map<int,int>mapp;
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    sett(1,n,1);

    for(int i=1;i<=n;i++){
        while(s1.size()&&a[s1.top()]<a[i]) s1.pop();
        if(s1.empty()) l[i]=0;
        else l[i]=s1.top();
        s1.push(i);
    }

    for(int i=n;i>=1;i--){
        while(s2.size()&&a[s2.top()]<a[i]) s2.pop();
        if(s2.empty()) r[i]=n+1;
        else r[i]=s2.top();
        s2.push(i);
        if(mapp.count(a[i])) continue;
        mapp[a[i]]=i;
    }
    int p=1,ans=0;
    while(p<n){
        if(r[p]==n+1){
            int d=findd(p+1,n,1,n,1);
            int h=mapp[d];
            for(int i=p+1;i<h;i++)
                ans+=d-a[i];
            p=h;
            continue;
        }
        int d=a[p];
        for(int i=p+1;i<r[p];i++)
            ans+=d-a[i];
        p=r[p];
    }
    printf("%d\n",ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值