标签:查询 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; }
标签:查询 uil tps 引用 scan 前缀 for 最小 int
原文地址:https://www.cnblogs.com/FrankChen831X/p/10784963.html