2019牛客多校第四场 C.sequence (笛卡尔树)

C.sequence

题意:有两个长度为n的数组a和b,求 max ⁡ 1 &lt; = l &lt; = r &lt; = n min ⁡ ( a l . . . r ) ∗ s u m ( b l . . . r ) \max_{1&lt;=l&lt;=r&lt;=n}\min(a_{l...r})*sum(b_{l...r}) max1<=l<=r<=nmin(al...r)sum(bl...r)
思路:题解中讲的挺清楚的,对于区间[l,r]无法直接枚举。从另一个角度考虑,一个最小值(记为min_x)会作用于一个区间,那么这个区间里面包含这min_x的子区间的最小值显然还是min_x。然后根据min_x的符号就可以分成三种情况。
1.min_x==0,这个最小值的造成区间的值就是0
2.min_x > 0,这时sum越大值越大,所以找区间的右子区间的最大sum,左子区间的最小sum,相减就是最大的区间和
3.min_x < 0,这时sum越小值越大,所以找区间的右子区间的最小sum,左子区间的最大sum,相减就是最小的区间和

主要问题是如何寻找一个最小值所影响的最大区间,笛卡尔树是一种方法,一个根会影响的区间是子树中最小的下标到最大的下标,正好是要求的最大区间的左右端点。

至于寻找最大sum最小sum,只需要对b数组的前缀和做rmq就好了,st表空间可能开不下了,还是线段树,树状数组之类的吧。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e6+5;
const ll INF=1e18;
template <typename _Tp> inline _Tp read(_Tp&x){
    char c11=getchar(),ob=0;x=0;
    while(c11^'-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x;
}
ll a[N],b[N],sum[N];
int st[N],top;
int lc[N],rc[N];
bool vis[N];
int ct_rt=0;
ll ans=0;
int n;

void build_ct(){
    int tmp;
    top=0;
    for(int i=1;i<=n;i++){
        vis[i]=false;
        lc[i]=rc[i]=0;
    }
    for(int i=1;i<=n;i++){
        tmp=top;
        while(tmp&&a[st[tmp-1]]>a[i])tmp--;
        if(tmp)rc[st[tmp-1]]=i;
        if(top>tmp)lc[i]=st[tmp];
        st[tmp++]=i;
        top=tmp;
    }
    for(int i=1;i<=n;i++){
        vis[lc[i]]=vis[rc[i]]=true;
    }
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            ct_rt=i;
            return;
        }
    }
}

#define lson rt<<1
#define rson rt<<1|1
ll maxn[N<<2],minn[N<<2];
void build(int rt,int l,int r){
    if(l==r){
        maxn[rt]=minn[rt]=sum[l];
        return;
    }
    int mid=(l+r)>>1;
    build(lson,l,mid);
    build(rson,mid+1,r);
    maxn[rt]=max(maxn[lson],maxn[rson]);
    minn[rt]=min(minn[lson],minn[rson]);
}

ll query_max(int rt,int l,int r,int x,int y){
    if(l>=x&&r<=y){
        return maxn[rt];
    }
    int mid=(l+r)>>1;
    if(y<=mid)return query_max(lson,l,mid,x,y);
    else if(x>mid) return query_max(rson,mid+1,r,x,y);
    else return max(query_max(lson,l,mid,x,mid),query_max(rson,mid+1,r,mid+1,y));
}

ll query_min(int rt,int l,int r,int x,int y){
    if(l>=x&&r<=y){
        return minn[rt];
    }
    int mid=(l+r)>>1;
    if(y<=mid)return query_min(lson,l,mid,x,y);
    else if(x>mid)return query_min(rson,mid+1,r,x,y);
    else return min(query_min(lson,l,mid,x,mid),query_min(rson,mid+1,r,mid+1,y));
}

void dfs(int rt,int l,int r){
    if(l>=r)return;
    if(a[rt]==0){
        ans=max(ans,0ll);
    } else if(a[rt]>0){
        ll ma=query_max(1,1,n,rt,r);
        ll mi=0;
        if(rt==1)mi=0;
        else if(l==1)mi=min(0ll,query_min(1,1,n,1,rt-1));
        else mi=query_min(1,1,n,l-1,rt-1);
        ans=max(ans,a[rt]*(ma-mi));
    } else{
        ll mi=query_min(1,1,n,rt,r);
        ll ma=0;
        if(rt==1)ma=0;
        else if(l==1)ma=max(0ll,query_max(1,1,n,1,rt-1));
        else ma=query_max(1,1,n,l-1,rt-1);
        ans=max(ans,a[rt]*(mi-ma));
    }
    dfs(lc[rt],l,rt-1);
    dfs(rc[rt],rt+1,r);
}

int main(){
    read(n);
    for(int i=1;i<=n;i++){
        read(a[i]);
    }
    build_ct();
    ans=-INF;
    sum[0]=0ll;
    for(int i=1;i<=n;i++){
        read(b[i]);
        ans=max(ans,b[i]*a[i]);
        sum[i]=sum[i-1]+b[i];
    }
    build(1,1,n);
    dfs(ct_rt,1,n);
    printf("%lld\n",ans);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值