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

题目链接: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;
}

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

时间: 2024-09-28 22:40:12

2019南昌网络赛-I(单调栈+线段树)的相关文章

ACM-ICPC 2019南昌网络赛I题 Yukino With Subinterval

ACM-ICPC 2019南昌网络赛I题 Yukino With Subinterval 题目大意:给一个长度为n,值域为[1, n]的序列{a},要求支持m次操作: 单点修改 1 pos val 询问子区间中某个值域的数的个数,连续的相同数字只记为一个.(即统计数字段的个数) 2 L R x y 数据范围: 1 ≤ n,m ≤ 2×10^5 1 ≤ a[i] ≤ n 解题思路: 连续重复的数字只记一次.所以考虑将每个数字段除第一个出现外的数字都删去(记为0).在读入操作的时候暴力模拟,同时维护

2019南昌网络赛-I. Yukino With Subinterval 线段树套树状数组

TMD...这题卡内存卡的真优秀... 所以以后还是别用主席树的写法...不然怎么死的都不知道... 树套树中,主席树方法开权值线段树...会造成空间的浪费...这道题内存卡的很紧... 由于树套树已经不需要持久化了,直接动态开点就完事了...用主席树方法开过不去,要么超内存,要么越界... 大概思路...这题要求的[L,R]区间内,满足x<=a[i]<=y的连续的段数, 这题大概是个套路题,我们很容易想到,我们把连续的区间变成单点,把一段区间,类似1 1 1 3 5 变成 1 0 0 3 5

【CF671E】Organizing a Race 单调栈+线段树

[CF671E]Organizing a Race 题意:n个城市排成一排,每个城市内都有一个加油站,赛车每次经过第i个城市时都会获得$g_i$升油.相邻两个城市之间由道路连接,第i个城市和第i+1个城市之间的道路长度为$w_i$,走一单位的路要花1升油.你想在某两个城市之间举办一场锦标赛.如果你选择的两个城市分别是a和b(a<b),则具体过程如下: 1. 赛车从a开始往右走一直走到b,走过城市时会在加油站加油,走过道路时会消耗油,且一开始时就已经在a处加完油了.你需要满足赛车能有足够的油能从a

单调栈+线段树

262144K 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. C

2017ICPC北京赛区网络赛 Minimum(数学+线段树)

描述 You are given a list of integers a0, a1, …, a2^k-1. You need to support two types of queries: 1. Output Minx,y∈[l,r] {ax?ay}. 2. Let ax=y. 输入 The first line is an integer T, indicating the number of test cases. (1≤T≤10). For each test case: The fi

2019 南昌网络赛icpc I题 cdq分治或分块

题意:给你一个数组,然后每次有两种操作,操作一是修改数组里的数,操作二是查询区间[ l , r ] 里有多少个子区间满足以下条件:1.子区间内的数全部相同.2.子区间内的数在x到y之间.3.子区间得是不能延伸的. 题目链接:https://nanti.jisuanke.com/t/41356 题解:首先转化问题,设 b[ i ] = a[i]==a[i-1] ? 0 : a[i],然后问题就变成了问询区间内有多少个x到y之间的数.(注意左端点特判)这不就是主席树....带修改...好,树状数组加

计蒜客38228 Max answer 单调栈 + 线段树

Max answer 与POJ 2796 Feel Good类似,但是这个题有负数,需要特殊处理一下 #include <bits/stdc++.h> #define DBG(x) cerr << #x << " = " << x << endl using namespace std; typedef long long LL; #define iall 1, n, 1 #define lrrt int l, int r,

【2019银川网络赛】L:Continuous Intervals

题目大意:给定一个长度为 N 的序列,定义连续区间 [l, r] 为:序列的一段子区间,满足 [l, r] 中的元素从小到大排序后,任意相邻两项的差值不超过1.求一共有多少个连续区间. 题解:单调栈 + 线段树 首先,对于区间计数类问题常规的思路是枚举区间的左端点或右端点,统计以该点为端点的区间个数,加入答案贡献. 对于这道题来说,不妨枚举答案的右端点 r,那么对于每个 r,需要快速得出有多少个左端点 l,使得区间 [l, r] 满足连续区间的性质.若能在 \(O(logn)\) 的时间内得出答

2019徐州网络赛 XKC&#39;s basketball team 线段树

网址:https://nanti.jisuanke.com/t/41387 题意: 大家好,我是训练时长两年半的个人练习生蔡徐坤,我的爱好是唱,跳,rap,篮球. 给出一段长度为$n,(n \leq 1e5)$的序列,对每一个数,求出它和它后面比它大$m$的数中间夹着的数的数量,没有输出$-1$. 题解: 直接建线段树,维护最大值,然后查询时对第$i$个数,搜索区间$[i,n]$之中大于$num[i]+m$的值的位置的最大值,具体操作是先限定区间,然后求出所有合法位置,取最大值,如果搜索不到则返