单调栈(区间最小值乘以区间间和)
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(); }