max answer 和 牛客多校第四场C

17 篇文章 0 订阅
11 篇文章 0 订阅

max answer

题意:给出一组数,求一个区间(l,r)中所有数之和与这个区间的最小数之积最大

做法:对于每个数a[i], 我们可以求出满足a[i]是最小数的 合法区间左边界l[i] 和 右边界r[i]; 然后对于每个a[i]接下来我们只要求

包含a[i]在内的在范围l[i]到r[i]内 最大子序列和为多少.

对于l[i]和r[i]我们可以通过单调栈求出,

每次加入一个元素的时候如果小于等于栈顶元素我们就把栈顶元素弹出直到为空或者

栈顶元素小于待加入的元素,此时l[i]为最左边界1 或者栈顶元素位置加 1;

r[i]同理 从后往前跑一遍单调栈,r[i]最右边界或者栈顶元素减 1;

case 1.对于a[i]>=0由于他在l[i]到r[i]中为最小值,则说明该区间每个数都大于等于0,则最大子序列和为所有数之和,ans更新为

所有值之和*a[i] 取max;

case 2 对于a[i]<0 由于序列可能正可能负,要使乘积最大,我们要使子序列和最小,我们可以用线段树维护前缀和数组中的

最大最小值,那么只要在区间l[i]到i-1找一个最大前缀和,在区间i到r[i]中找一个最小的前缀和,然后后者减去前者便是包含a[i]

在内的最小子序和。然后与a[i]相乘的结果与ans 取max;

这题也可以枚举在i+1到r[i]中枚举右端点不断累加求前缀和,然后取最小值m1, 在l[i]到i-1中不断求后缀和取最小值m2,则最小

子序列和为m1+m2+a[i] (个人感觉会T可能数据里的负数比较少吧)

#include<bits/stdc++.h>
using namespace std;
typedef  long long ll;
const int maxn=500005;
const ll inf=0xfffffffffff;
ll l[500005], r[500005], a[500005], sum[500005];
int n;
struct segment
{
    int l,r;
    ll mi,mx;
}t[maxn*4];
void pushup(int o)
{
    t[o].mi=min(t[o<<1].mi,t[o<<1|1].mi);
    t[o].mx=max(t[o<<1].mx, t[o<<1|1].mx);
}
void build(int o, int l, int r)
{
    t[o].l=l;
    t[o].r=r;
    if(l==r)
    {
        t[o].mi=t[o].mx=sum[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(o<<1,l,mid);
    build(o<<1|1,mid+1,r);
    pushup(o);
}
ll querymi(int o, int l, int r)
{
    if(l<=t[o].l&&t[o].r<=r)
    {
        return t[o].mi;
    }
    int mid=(t[o].l+t[o].r)>>1;
    ll Mi=inf;
    if(l<=mid)Mi=min(Mi,querymi(o<<1,l,r));
    if(r>mid)Mi=min(Mi,querymi(o<<1|1,l,r));
    return Mi;
}
ll querymx(int o,int l, int r)
{
    if(l<=t[o].l&&t[o].r<=r)
    {
        return t[o].mx;
    }
    int mid=(t[o].l+t[o].r)>>1;
    ll Mx=-inf;
    if(l<=mid)Mx=max(Mx,querymx(o<<1,l,r));
    if(r>mid)Mx=max(Mx,querymx(o<<1|1,l,r));
    return Mx;
}
int main()
{
    scanf("%d",&n);
    stack<ll>q;
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
    build(1,1,n);
    for(int i=1;i<=n;i++)
    {
        while(!q.empty()&&a[q.top()]>=a[i])
        {
            q.pop();
        }
        if(q.empty())l[i]=1;
        else l[i]=q.top()+1;
        q.push(i);
    }
    while(!q.empty())q.pop();
    for(int i=n;i>=1;i--)
    {
        while(!q.empty()&&a[q.top()]>=a[i])
        {
            q.pop();
        }
        if(q.empty())r[i]=n;
        else r[i]=q.top()-1;
        q.push(i);
    }
    ll ans=-inf;
    for(int i=1;i<=n;i++)
    {
        if(a[i]>=0)
        {
            ans=max(ans,(sum[r[i]]-sum[l[i]]+a[l[i]])*a[i]);
        }
        else
        {
            ans=max(ans, (querymi(1,i,r[i])-querymx(1,1,i-1))*a[i]);
        }
    }
    cout<<ans<<endl;
    return 0;
}
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll l[500005], r[500005], a[500005], sum[500005];
int n;
struct segment
{
    int l,r;
    ll mi,mx;
}t[maxn*4];
void pushup(int o)
{
    t[o].mi=min(t[o<<1].mi,t[o<<1|1].mi);
    t[o].mx=max(t[o<<1].mx, t[o<<1|1].mx);
}
void build(int o, int l, int r)
{
    t[o].l=l;
    t[o].r=r;
    if(l==r)
    {
        t[o].mi=t[o].mx=sum[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(o<<1,l,mid);
    build(o<<1|1,mid+1,r);
    pushup(o);
}
ll querymi(int o, int l, int r)
{
    if(l<=t[o].l&&t[o].r<=r)
    {
        return t[o].mi;
    }
    int mid=(t[o].l+t[o].r)>>1;
    ll Mi=inf;
    if(l<=mid)Mi=min(Mi,querymi(o<<1,l,r));
    if(r>mid)Mi=min(Mi,querymi(o<<1|1,l,r));
    return Mi;
}

int getnum(int n,int x,int y)
{
    int r=0;
    if(x<=y & x+y <= n+1)
    {
        r = x;
        return  4*(r-1)*n - 4*(r-1)*(r-1) +1 + y-r;
    }
    if(x<=y & x+y >= n+1)
    {
        r = n- y + 1;
        return 4*(r-1)*n - 4*(r-1)*(r-1) + 1 + n-2*r + 1 + x - r;
    }
    if(x>=y & x+y >= n+1)
    {
        r = n - x +1;
        return 4*(r-1)*n - 4*(r-1)*(r-1) + 1 + 3*n-6*r + 3 - y + r;
    }
    if(x>=y & x+y <= n+1)
    {
        r = y;
        return 4*(r-1)*n - 4*(r-1)*(r-1) + 1 + 4*n-8*r + 4  - x + r;
    }
    return 0;
}


int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            printf("%d %d %d\n",i,j,getnum(n,i,j));
        }
    }
    return 0;
}

牛客多校第四场C 

题意给你两个序列A,B 求在一个区间(l,r)内a[i]的最小值和b[i]的序列之和的乘积

和上面的做法几乎一样,同样用单调栈求出a的合法区间l[i],r[i],然后对a[i]>=0和a[i]<0 两种情况分别求最大子序列和与最小子序列和, 用线段树维护b数组的前缀和的最大最小值,则 最大子序列和等于i~r[i]中最大前缀和减去l[i]-1~i-1中最小前缀和 , 最小子序列和等于i~r[i]中最小前缀和减去l[i]-1~i-1中最大前缀和. 分别更新答案

由于查找前缀和的时候可能找到0点,所以线段树要从0开始建,并且这题比较卡复杂度,可以把结构体里的左右边界l,r写到函数的参数里,减少节点维护的信息个数

#include<bits/stdc++.h>
using namespace std;
typedef  long long ll;
const int maxn=3000006;
const ll inf=0xfffffffffff;
ll l[maxn], r[maxn], a[maxn], sum[maxn],b[maxn];
int n;
struct segment
{
    ll mi,mx;
}t[maxn*4];
void pushup(int o)
{
    t[o].mi=min(t[o<<1].mi,t[o<<1|1].mi);
    t[o].mx=max(t[o<<1].mx, t[o<<1|1].mx);
}
void build(int o, int l, int r)
{
    //t[o].l=l;
    //t[o].r=r;
    if(l==r)
    {
        t[o].mi=t[o].mx=sum[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(o<<1,l,mid);
    build(o<<1|1,mid+1,r);
    pushup(o);
}
ll querymi(int o, int l, int r,int L,int R)
{
    if(l<=L&&R<=r)
    {
        return t[o].mi;
    }
    int mid=(L+R)>>1;
    ll Mi=inf;
    if(l<=mid)Mi=min(Mi,querymi(o<<1,l,r,L,mid));
    if(r>mid)Mi=min(Mi,querymi(o<<1|1,l,r,mid+1,R));
    return Mi;
}
ll querymx(int o,int l, int r,int L,int R)
{
    if(l<=L&&R<=r)
    {
        return t[o].mx;
    }
    int mid=(L+R)>>1;
    ll Mx=-inf;
    if(l<=mid)Mx=max(Mx,querymx(o<<1,l,r,L,mid));
    if(r>mid)Mx=max(Mx,querymx(o<<1|1,l,r,mid+1,R));
    return Mx;
}
int main()
{
    scanf("%d",&n);
    stack<ll>q;
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+b[i];
    sum[0]=0;
    build(1,0,n);
    for(int i=1;i<=n;i++)
    {
        while(!q.empty()&&a[q.top()]>=a[i])
        {
            q.pop();
        }
        if(q.empty())l[i]=1;
        else l[i]=q.top()+1;
        q.push(i);
    }
    while(!q.empty())q.pop();
    for(int i=n;i>=1;i--)
    {
        while(!q.empty()&&a[q.top()]>=a[i])
        {
            q.pop();
        }
        if(q.empty())r[i]=n;
        else r[i]=q.top()-1;
        q.push(i);
    }
    ll ans=-inf;
    for(int i=1;i<=n;i++)
    {
        if(a[i]>=0)
        {
            //cout<<ans<<" "<<111<<endl;
            ans=max(ans,(querymx(1,i,r[i],0,n)-querymi(1,l[i]-1,i-1,0,n))*a[i]);
        }
        else
        {
            ans=max(ans,(querymi(1,i,r[i],0,n)-querymx(1,l[i]-1,i-1,0,n))*a[i]);
        }
    }
    cout<<ans<<endl;
    return 0;
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值