【POJ 2823】 Feel Good 單調棧 詳解

Description

Bill is developing a new mathematical theory for human emotions. His recent investigations are dedicated to studying how good or bad days influent people’s memories about some period of life.

A new idea Bill has recently developed assigns a non-negative integer value to each day of human life.

Bill calls this value the emotional value of the day. The greater the emotional value is, the better the daywas. Bill suggests that the value of some period of human life is proportional to the sum of the emotional values of the days in the given period, multiplied by the smallest emotional value of the day in it. This schema reflects that good on average period can be greatly spoiled by one very bad day.

Now Bill is planning to investigate his own life and find the period of his life that had the greatest value. Help him to do so.
Input

The first line of the input contains n - the number of days of Bill’s life he is planning to investigate(1 <= n <= 100 000). The rest of the file contains n integer numbers a1, a2, … an ranging from 0 to 106 - the emotional values of the days. Numbers are separated by spaces and/or line breaks.
Output

Print the greatest value of some period of Bill’s life in the first line. And on the second line print two numbers l and r such that the period from l-th to r-th day of Bill’s life(inclusive) has the greatest possible value. If there are multiple periods with the greatest possible value,then print any one of them.
Sample Input

6
3 1 6 4 5 2
Sample Output

60
3 5

題意:求一個區間,使得區間和乘上其區間內最小元素的結果最大

思路(單調棧):

(廢話有點多,知道單調棧的原理的直接看第二段前面)
這和HDU 1506這道題幾乎一個考點。
要想,我們要求的一個區間內最小值乘上區間和的結果,那麼這個區間的邊界肯定是比最小值還小的!爲什麼?因爲如果邊界大於區間內最小值的話,完全可以將這個區間邊界包含進區間內,這樣還對我們的區間和有貢獻。所以,問題就轉化成了,求每個位置左右兩邊第一個比它小的元素的位置。
這就用到了單調棧,按照棧底到棧頂單調遞減的排列,拿找左邊第一小L爲例,每次在棧中找到第一個小於它的元素,而其他全部pop掉,這時候取棧頂就是當前這個元素的左邊第一小,然後將這個元素push進棧即可。爲什麼在棧中找當前L的時候可以把棧中比它大的全部pop掉呢?首先,這些比它大的對它本身沒有作用,取不到。其次,i+1的位置,如果i+1的值比i的小,那麼左邊比i+1還小的元素肯定在i的L的左邊!有點繞,比如1 4 2 4 3 2 ,我們在i=5的時候(3),找左邊第一小,是i=3這個位置(2),那到i=6的時候,它因爲比i=5的值小,所以本來就比前面那個元素大的元素就完全不用考慮了,比如這裏就不用再比較一次i=4的位置,因爲它本身就比i=5的元素大,自然比i=6的元素大。我們這個時候只需要在之前L = 3的位置繼續往左找就行了。

AC代碼:

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef pair<int,int> PII;
typedef long long ll;
const int maxn = 1e6+5;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') ch = getchar();while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };
ll l[maxn];
ll r[maxn];
ll sum[maxn];
ll a[maxn];
ll n; stack<ll> s;

void solve()
{
    rep(i,1,n)
    {
        while(s.size()&&a[s.top()]>=a[i]) s.pop();
        if(s.empty()) l[i] = 0;
        else l[i] = s.top();
        s.push(i);
    }
    while(!s.empty()) s.pop();
    per(i,n,1)
    {
        while(s.size()&&a[s.top()]>=a[i]) s.pop();
        if(s.empty()) r[i] = n+1;
        else r[i] = s.top();
        s.push(i);
    }
    ll L = 0, R = 0; ll ans = -1;
    rep(i,1,n)
    {
       // cout<<l[i]<<' '<<r[i]<<endl;
        ll cur =  (sum[r[i]-1] -sum[l[i]])*a[i];
        if(ans<cur)
        {
            ans = cur;
            L = l[i]+1; R = r[i]-1;
        }
    }
    cout<<ans<<'\n';
    cout<<L<<' '<<R<<'\n';

}

int main()
{
    FAST;
    n = read();
    sum[0] = 0;
    rep(i,1,n)
    {
        a[i] = read();
        sum[i] = sum[i-1] + a[i];
    }
    solve();
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章