题目大意:给一个非负整数序列,求出一个使得区间和乘以区间最小值最大的区间。
题目分析:单调队列。维护两个数组,l[i]表示以a[i]为最小值的左半区间的最左边端点,r[i]表示以a[i]为最小值的右半区间的最右边端点,l[i]和r[i]合起来便是以a[i]为最小值的整个区间。枚举一遍 i 即可。
注意:UVA上的这道题有个大大的坑,明明说可以输出任意一个区间(多个解时),实际上是骗人的!!!
代码如下:
# include<iostream> # include<cstdio> # include<cstring> # include<algorithm> using namespace std; # define LL long long struct Num { int val,id; }; Num a[100005],que[100005]; int n,Left[100005],Right[100005]; long long sum[100005]; void f() { int head=0,tail=-1; for(int i=1;i<=n;++i){ Left[i]=i; while(head<=tail&&que[tail].val>=a[i].val) --tail; if(head<=tail) Left[i]=que[tail].id+1; else Left[i]=1; que[++tail]=a[i]; } head=0,tail=-1; for(int i=n;i>=1;--i){ Right[i]=i; while(head<=tail&&que[tail].val>=a[i].val) --tail; if(head<=tail) Right[i]=que[tail].id-1; else Right[i]=n; que[++tail]=a[i]; } } int main() { int flag=0,p; while(~scanf("%d",&n)) { if(flag) printf("\n"); flag=1; sum[0]=0; for(int i=1;i<=n;++i){ scanf("%d",&a[i].val); a[i].id=i; sum[i]=(LL)a[i].val+sum[i-1]; } f(); int ansl,ansr; long long ans=-1; for(int i=1;i<=n;++i) if(ans<(LL)a[i].val*(sum[Right[i]]-sum[Left[i]-1])) ans=(LL)a[i].val*(sum[Right[i]]-sum[Left[i]-1]),ansl=Left[i],ansr=Right[i]; printf("%lld\n",ans); if(ans==0) printf("1 1\n"); else printf("%d %d\n",ansl,ansr); } return 0; }
时间: 2024-10-30 04:39:20