码迷,mamicode.com
首页 > 其他好文 > 详细

2019南昌网络赛-I(单调栈+线段树)

时间:2019-04-28 17:12:38      阅读:212      评论:0      收藏:0      [点我收藏+]

标签:查询   uil   tps   引用   scan   前缀   for   最小   int   

题目链接:https://nanti.jisuanke.com/t/38228

题意:定义一段区间的值为该区间的和×该区间的最小值,求给定数组的最大的区间值。

思路:比赛时还不会线段树,和队友在这题上弄了3小时,思路大体都是对的,但就是没法实现。这几天恶补线段树。

   首先可以利用单调栈来查找满足a[i]为最小值的最大区间L[i]~R[i]。然后利用线段树求一个段的和sum、最小前缀lsum和最小后缀rsum。然后遍历a[i]:

    a[i]>0:最优为sum(L[i],R[i])*a[i]

    a[i]<0:最优为(sumr(L[i],i)+suml(i,R[i]-i)*a[i]

   这里用线段树查询可以用传递引用来求lsum和rsum,因为我们查询一段区间是从左向右查询的,或者可以用三个全局变量Sum、Lsum、Rsum记录当前已找到的区间的对应属性也行。

AC代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=500005;
typedef long long LL;

struct node{
    int l,r;
    LL sum,lsum,rsum;  //sum为区间和,lsum最小前缀,rsum最小后缀
}tr[maxn<<2];
//L[i]~R[i]为满足a[i]为最小的最大区间
int n,p,a[maxn],L[maxn],R[maxn],stk[maxn];
LL ans;

node gets(node a,node b){
    node t;
    t.l=a.l,t.r=b.r;
    t.sum=a.sum+b.sum;
    t.lsum=min(a.lsum,a.sum+b.lsum);
    t.rsum=min(b.rsum,b.sum+a.rsum);
    return t;
}

void build(int v,int l,int r){
    tr[v].l=l,tr[v].r=r;
    if(l==r){
        tr[v].sum=a[r];
        tr[v].lsum=min(a[r]*1LL,0LL);
        tr[v].rsum=min(a[r]*1LL,0LL);
        return;
    }
    int mid=(l+r)>>1;
    build(v<<1,l,mid);
    build(v<<1|1,mid+1,r);
    tr[v]=gets(tr[v<<1],tr[v<<1|1]);
}

void query(node &x,int v,int l,int r){
    if(l<=tr[v].l&&r>=tr[v].r){
        x=gets(x,tr[v]);
        return;
    }
    int mid=(tr[v].l+tr[v].r)>>1;
    if(l<=mid) query(x,v<<1,l,r);
    if(r>mid) query(x,v<<1|1,l,r);
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
        scanf("%d",&a[i]);
    a[0]=a[n+1]=0xcfcfcfcf;
    stk[p=0]=0;   //利用单调栈求L[i],R[i]
    for(int i=1;i<=n;++i){
        while(a[stk[p]]>=a[i]) --p;
        L[i]=stk[p]+1;
        stk[++p]=i;
    }
    stk[p=0]=n+1;
    for(int i=n;i>=1;--i){
        while(a[stk[p]]>=a[i]) --p;
        R[i]=stk[p]-1;
        stk[++p]=i;
    }
    build(1,1,n);
    for(int i=1;i<=n;++i){
        if(a[i]>0){
            node t;
            t.sum=t.lsum=t.rsum=0;
            query(t,1,L[i],R[i]);
            if(a[i]*t.sum>ans) ans=a[i]*t.sum;
        }
        else if(a[i]<0){
            LL tmp=0;
            node t;
            t.sum=t.lsum=t.rsum=0;
            query(t,1,L[i],i);
            tmp+=t.rsum;
            t.sum=t.lsum=t.rsum=0;
            query(t,1,i,R[i]);
            tmp+=t.lsum;
            tmp-=a[i];
            if(tmp*a[i]>ans) ans=tmp*a[i];
        }
    }
    printf("%lld\n",ans);
    return 0;
}

 

2019南昌网络赛-I(单调栈+线段树)

标签:查询   uil   tps   引用   scan   前缀   for   最小   int   

原文地址:https://www.cnblogs.com/FrankChen831X/p/10784963.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!