单调栈(区间最小值乘以区间间和)
http://poj.org/problem?id=2796
题意:给出一个序列,求出一个子序列,使得这个序列中的最小值乘以这个序列的和的值最大。
解法:单调递增栈:每一个元素进栈前将元素剔除的过程是该元素在向左扩展,每一个元素在站内被某一元素剔除的过程为该元素的右扩展。
//#include<bits/stdc++.h>
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<cmath>
using namespace std ;
typedef long long ll ;
const int N = 100010, M = 200010 ;
int a[N] ;
ll len[N];//以i为最小值,当前i所能向左向右扩展的和
int st[N] , top ;
int n ;
pair<int,int>b[N];
void solve(){
for(int i = 0 ; i < n ; i++){
scanf("%d" , &a[i]);
len[i] = a[i] ;
}
top = 0 ;
ll cnt = 0 ;
for(int i = 0 ; i < n ; i++){
cnt = 0 ;//当前右边扩展长度
int l = i ;
while(top && a[st[top]] >= a[i]){
len[st[top]] += cnt ;//我被弹(扩展右边)
cnt = len[st[top]] ;
b[st[top]].second = i - 1 ;
l = b[st[top]].first ;//继承弹掉的左区间
top-- ;
}
len[i] += cnt ;//我弹别人(扩展左边)
b[i].first = l ;
st[++top] = i ;
}
cnt = 0 ;
int r = n - 1;
while(top){
len[st[top]] += cnt ;
cnt = len[st[top]] ;
b[st[top]].second = r ;
top-- ;
}
int ans = 0 ;
for(int i = 0 ; i < n ; i++){
if(len[ans] * a[ans] < len[i] * a[i]){
ans = i ;
}
}
cout << len[ans]*a[ans] << endl << b[ans].first + 1 << " " << b[ans].second + 1 << endl;
}
signed main(){
#ifdef ONLINE_JUDGE
#else
freopen("D:\\c++\\in.txt", "r", stdin);
//freopen("D:\\c++\\out.txt", "w", stdout);
#endif
while(~scanf("%d" , &n))
solve();
}

浙公网安备 33010602011771号