The Preliminary Contest for ICPC China Nanchang National Invitational I题

Alice has a magic array. She suggests that the value of a interval is equal to the sum of the values in the interval, multiplied by the smallest value in the interval.

Now she is planning to find the max value of the intervals in her array. Can you help her?

Input

First line contains an integer n(1 \le n \le 5 \times 10 ^5n(1≤n≤5×105).

Second line contains nn integers represent the array a (-10^5 \le a_i \le 10^5)a(−105≤ai?≤105).

Output

One line contains an integer represent the answer of the array.

样例输入复制

5
1 2 3 4 5

样例输出复制

36

题意:给你一个长度为n的序列,让你找一段区间使得该段 区间和 和 它的区间最小值 乘积最大,并输出最大值。

思路:因为我们不知道当最小值是多少时 以它为最小值的区间 的和 与 它的乘积最大,所以我们只能枚举每个位置,以每个位置为最小值,求出以它为最小值的最大值

当我们以当前位置的值作为最小值时,我们怎么求出以它为最小值的最大区间和呢首先,我们要知道以当前值为区间最小值时,它可能的区间范围

以上图为例 ,当我们以第三个位置的值为最小值时,它的区间范围为(2,5)我们可以在区间(2,5)内找到一段区间和最大的区间(一定要包含第三号位置),就找到了以第三个位置为最小值的题目要找的最大值。找这段和最大的区间时我们可以用前缀和建线段树来求出

当当前位置值为正数时,我们从当前位置的左边的区间找出最小的前缀和,从当前位置的右边区间找出最大的前缀和,那么,用右边找到的值减去左边的值就是我们满足我们要求的最大的区间和(这样用右边找到的前缀和减去左边找到的前缀和不就是在中间我们要找的包含当前位置的区间最大和吗,负数的时候相反)

代码:
#include<cstdio>
#include<algorithm>
#include<stack>
#define ll long long
using namespace std;
struct point{
    ll a;//值
    ll x;//位置
}s[500010];
stack<point> st;
struct{
    ll l,r;//记录以当前位置为最小值区可以取区间的范围
}bd[500010];
ll sum[500010];//前缀和
struct{
    ll mx,mn;//记录前缀和的最大值和最小值
}tree[2000010];
void build(ll l,ll r,ll k){
    if(l==r){
        tree[k].mn=tree[k].mx=sum[l];
        return ;
    }
    ll mid=(l+r)>>1;
    build(l,mid,k*2);
    build(mid+1,r,k*2+1);
    tree[k].mx=max(tree[k*2].mx,tree[k*2+1].mx);
    tree[k].mn=min(tree[k*2].mn,tree[k*2+1].mn);
    return ;
}
ll query1(ll l,ll r,ll k,ll L,ll R){//找最大值
    //printf("WW%d %d %d %d\n",l,r,L,R);
    if(l>=L&&r<=R){
        return tree[k].mx;
    }
    ll a,b;
    a=-1e18;
    b=-1e18;
    ll mid=(l+r)>>1;
    if(L<=mid)
    a=query1(l,mid,k*2,L,R);
    if(R>mid)
    b=query1(mid+1,r,k*2+1,L,R);
    return max(a,b);
}
ll query2(ll l,ll r,ll k,ll L,ll R){//找最小值
    if(l>=L&&r<=R){
        return tree[k].mn;
    }

    //printf("EE%d %d %d %d\n",l,r,L,R);
    ll a,b;
    a=1e18;
    b=1e18;
    ll mid=(l+r)>>1;
    if(L<=mid)
    a=query2(l,mid,k*2,L,R);
    if(R>mid)
    b=query2(mid+1,r,k*2+1,L,R);
    return min(a,b);
}
int main(){
    ll n;
    scanf("%lld",&n);
    sum[0]=0;
    for(ll i=1;i<=n;i++){
        scanf("%lld",&s[i].a);
        s[i].x=i;
        sum[i]=sum[i-1]+s[i].a;
    }
    for(ll i=1;i<=n;i++){//单调栈求范围
        if(st.empty()){//如果当前栈为空,当前要放入位置的左极限位置就是自己的位置
            bd[s[i].x].l=i;
            st.push(s[i]);
        }
        else{
            point tp=st.top();
            if(tp.a==s[i].a){//如果当前位置的值与栈顶的值一样,那么当前要放入位置的左极限就和栈顶位置的左极限相同
                bd[s[i].x].l=bd[tp.x].l;
                st.push(s[i]);
            }
            else if(tp.a<s[i].a){//如果当前放入位置的值大于栈顶位置的值,那么当前放入位置的左极限位置就是自己的位置
                bd[s[i].x].l=i;
                st.push(s[i]);
            }
            else{//如果当前放入位置的值小于栈顶位置的值
                st.pop();
                ll r=tp.x;
                ll l=bd[tp.x].l;
                bd[tp.x].r=r;//栈顶位置的右极限就是当前栈顶位置的位置
                if(!st.empty())
                tp=st.top();
                while(!st.empty()&&tp.a>s[i].a){//如果下一个栈顶位置的值还是大于当前位置
                    st.pop();
                    bd[tp.x].r=r;//后面的栈顶可以到的右极限都和第一次栈顶位置的右极限相同,因为栈是从小到上递增的
                    l=bd[tp.x].l;//更新要放入的当前位置可以到的左极限
                    if(!st.empty())
                    tp=st.top();
                }
                bd[s[i].x].l=l;
                st.push(s[i]);
            }
        }
    }
    if(!st.empty()){//更新
        point tp=st.top();
        ll r=tp.x;
        while(!st.empty()){
            tp=st.top();
            st.pop();
            bd[tp.x].r=r;
        }
    }
//    for(ll i=1;i<=n;i++){
//        printf("%d %d %d\n",i,bd[i].l,bd[i].r);
//    }
    build(0,n,1);
    ll ans=-1e18;
//    printf("OK");
    for(ll i=1;i<=n;i++){
        ll r1,l1;
        if(s[i].a>0){
            r1=query1(0,n,1,s[i].x,bd[s[i].x].r);
            l1=query2(0,n,1,bd[s[i].x].l-1,s[i].x-1);
            //printf("%lld %lld\n",l1,r1);
            ans=max((r1-l1)*s[i].a,ans);
        }
        else if(s[i].a<0){
            r1=query2(0,n,1,s[i].x,bd[s[i].x].r);
            l1=query1(0,n,1,bd[s[i].x].l-1,s[i].x-1);
            //printf("%lld %lld\n",l1,r1);
            ans=max((r1-l1)*s[i].a,ans);
        }
        else
        ans=max(0LL,ans);
    }
    printf("%lld\n",ans);
} 


原文地址:https://www.cnblogs.com/cglongge/p/10746281.html

时间: 2024-11-08 19:47:20

The Preliminary Contest for ICPC China Nanchang National Invitational I题的相关文章

The Preliminary Contest for ICPC China Nanchang National Invitational and International Silk-Road Programming Contest

打网络赛 比赛前的准备工作要做好 确保 c++/java/python的编译器能用 打好模板,放在桌面 A. PERFECT NUMBER PROBLEM 1 #include <cstdio> 2 #include <cstdlib> 3 #include <cmath> 4 #include <cstring> 5 #include <string> 6 #include <algorithm> 7 #include <se

The Preliminary Contest for ICPC China Nanchang National Invitational I.Max answer单调栈

题面 题意:一个5e5的数组,定义一个区间的值为 这个区间的和*这个区间的最小值,注意数组值有负数有正数,求所有区间中最大的值 题解:如果全是正数,那就是原题 POJ2796 单调栈做一下就ok 我们现在有负数,考虑这段区间,他的和必须是负数,由于导致和为负数,最小值一定也是负数, 那对于这样一个和为负的区间进行扩展的时候,遇见下一个数,是负数,我们一定会扩展,无论这个负数大小 遇见下一个是正数,如果和没有变正,那就可以继续扩展下去(不更新答案罢了) 所以我们对于那些和为负的区间,单独统计一下答

The 2019 ICPC China Nanchang National Invitational and International Silk-Road Programming Contest C. Xyjj’s sequence(动态规划+欧拉降幂)

题目链接:https://nanti.jisuanke.com/t/40255 中文题面: 解题思路:先用欧拉降幂求出A,B两个序列,定义dp[0][i][j]为取A的前i个元素,B的前j个元素,且C的最后一个元素为B[j],同理dp[1][i][j]为取A的前i个元素,B的前j个元素,且C的最后一个元素为A[i],那么就很容易得到状态转移方程.那么最后答案即为max(dp[0][n][n],dp[1][n][n]).还有值得注意的是:该题需要使用滚动数组,不然会超内存. 在此贴两个关于欧拉降幂

The 2019 ICPC China Nanchang National Invitational and International Silk-Road Programming Contest E. Interesting Trip 长链剖分

题库链接 考虑莫比乌斯, 套上去之后就是变成了统计长度为d的一共有多少路径, 直接长链剖分, 在计蒜客上极度卡常, 卡了一万年才卡过去, 现场好像还有用点分治过去的, 这都能过?? #include<bits/stdc++.h> #define LL long long using namespace std; const int N = (int)5e5 + 7; const int M = 30000; int n, d, a[N], vis[N], miu[M + 1]; int now

The 2019 ICPC China Nanchang National Invitational and International Silk-Road Programming Contest - F.Sequence(打表+线段树)

题意:给你一个长度为$n$的数组,定义函数$f(l,r)=a_{l} \oplus a_{l+1} \oplus...\oplus a_{r}$,$F(l,r)=f(l,l)\oplus f(l,l+1)\oplus ...\oplus f(l,r)\oplus f(l+1,l+1)\oplus ...f(l+1,r)\oplus ...\oplus f(r,r)$,有两种操作,第一种将数组中某个元素$a[x]$变为$y$,第二种计算$F(l,r)$的值. 思路:打表后发现只有当$l$和$r$同

The Preliminary Contest for ICPC Asia Nanchang 2019

目录 Solutions B. Fire-Fighting Hero E. Magic Master Link Solutions B. Fire-Fighting Hero 题意: 思路: dijkstra最短路 先以 hero 为起点 跑一遍 dijkstra 建立 起点 \(p\) 并与各 fire-fighting point 建立权为 \(0\) 的边,跑一遍 dijkstra 第二次 dijkstra 中 dis[i] 为各 fire-fighting point 到各点的最短路径中

C:Dawn-K&#39;s water (The Preliminary Contest for ICPC Asia Shenyang 2019)

Dawn-K recently discovered a very magical phenomenon in the supermarket of Northeastern University: The large package is not necessarily more expensive than the small package. On this day, Dawn-K came to the supermarket to buy mineral water, he found

The Preliminary Contest for ICPC Asia Shenyang 2019

The Preliminary Contest for ICPC Asia Shenyang 2019 Texas hold'em Poker #include <bits/stdc++.h> using namespace std; const int maxn=1e6+10; int num[1000]; int shun(){ for (int i=15;i>=5;i--){ if (num[i]&&num[i-1]&&num[i-2]&&a

The Preliminary Contest for ICPC Asia Shanghai 2019 C Triple(FFT+暴力)

The Preliminary Contest for ICPC Asia Shanghai 2019 C Triple(FFT+暴力) 传送门:https://nanti.jisuanke.com/t/41400 题意: 给你三个数组a,b,c,要你求有多少个三元组(i,j,k),使得 \[ \begin{array}{l}{\left|A_{i}-B_{j}\right| \leq C_{k}, \text { and }} \\ {\left|B_{j}-C_{k}\right| \leq