1. 程式人生 > >求任意一個區間中的最大值,最小值 - 單調棧

求任意一個區間中的最大值,最小值 - 單調棧

連結:https://ac.nowcoder.com/acm/contest/223/C
來源:牛客網

題目描述

給出長度為n的序列a,其中第i個元素為 ,定義區間(l,r)的價值為
請你計算出

輸入描述:

第一行輸入資料組數T
對於每組資料,第一行為一個整數n,表示序列長度
接下來一行有n個數,表示序列內的元素

輸出描述:

對於每組資料,輸出一個整數表示答案
示例1

輸入

複製
3
3
4 2 3
5
1 8 4 3 9
20
2 8 15 1 10 5 19 19 3 5 6 6 2 8 2 12 16 3 8 17 

輸出

複製
5
57
2712

說明

對於一組測試資料的解釋:
區間[1, 2]的貢獻為:4 - 2 = 2
區間[1, 3]的貢獻為:4 - 2 = 2
區間[2, 3]的貢獻為:3 - 2 = 1
2 + 1 + 2 = 5.

備註:

不保證資料隨機生成!   題意 : 題目經過幾步簡單的化簡便可以知道我們要求的是任意一個區間中的最大值與最小值的差去累加 思路分析:暴力的 n^2 做法比較好想到   將上面的式子再進行一步化簡可以得到的任意一個區間的最大值總和 - 任意一個區間最小值的總和   對於上式的兩部分我們可以分別去求,用單調棧即可   例如在求最大值的和累加時,去維護一個單調遞減的棧,具體看程式碼就懂了
#define ll long long
const ll maxn = 1e5+5;

ll n;
ll a[maxn], sta[maxn];

void solve() {
    ll top = 0;
    ll sum = 0, ans = 0;
    for(ll i = 1; i <= n; i++){ // 下降
        while(top > 0 && a[sta[top]] < a[i]) {
            ll num = a[sta[top]];
            sum -= num*(sta[top]-sta[top-1]);
            top--;
        } 
        sta[++top] = i;
        sum += a[i]*(sta[top]-sta[top-1]);
        ans += sum;
    }
    
    top = 0, sum = 0;
    for(ll i = 1; i <= n; i++){ // 上升
        while(top > 0 && a[sta[top]] > a[i]){
            ll num = a[sta[top]];
            sum -= num*(sta[top]-sta[top-1]);
            top--;
        }
        sta[++top] = i;
        sum += a[i]*(sta[top]-sta[top-1]);
        ans -= sum;
    }
    cout << ans << '\n';
}

int main() {
    ll t;
    
    cin >> t;
    while(t--){
        scanf("%lld", &n);
        for(ll i = 1; i <= n; i++){
            scanf("%d", &a[i]);
        }        
        solve();
    }
    return 0;
}