noip提升組模擬賽(QBXT)T2

T2count題解

【 問題描述】:ios

小 A 是一名熱衷於優化各類算法的 OIER,有一天他給了你一個隨機生成的 1~n 的排列, 並定 義區間[l,r]的價值爲:
\[ \huge C_{l,r}=\max(a_i-a_j|l \le i,j \le r ) \]
他想請你告訴他, 全部區間的價值的總和爲多少git

【 輸入】算法

第一行一個數 T(<=10), 表示數據組數 對於每一組數據: 第一行一個數 n( 1<=n,m<=100,000) 第二行 n 個數 a1...an, 表示一個 1~n 的隨機的排列優化

【 輸出】spa

對於每組數據輸出一個數, 表示答案code

【 輸入樣例】blog

1
4
3 2 4 1

【 輸出樣例】get

14

【 數據範圍】string

對於 60%的數據: n<=1000it

對於 100%的數據, n<=100,000

咱們先看普通的暴力:

\(mi[l][r]\)表示從\(l\)\(r\)區間的最小值

\(mx[l][r]\)表示從\(l\)\(r\)區間的最大值

則答案爲:
\[ \large \sum_{l=1}^{n}\sum_{r=l}^{n}(mx[l][r]-mi[l][r]) \]
可是仔細觀察式子咱們能夠發現:
\[ \sum_{l=1}^{n}\sum_{r=l}^{n}(mx[l][r]-mi[l][r])=\sum_{l=1}^{n}\sum_{r=l}^{n}mx[l][r]-\sum_{l=1}^{n}\sum_{r=l}^{n}mi[l][r] \]
而後mx和mi的部分咱們能夠單獨求

因此以最大值爲例子

一個點能夠管轄的範圍爲左邊第一個比他大的點到右邊第一個比他大的點

咱們設\(l[i]\)爲左邊第一個比\(a[i]\)大的位置\(r[i]\)爲右邊第一個比\(a[i]\)大的位置

fuck.png

則只要知足\(l[i]<x\le i\)而且\(i\le y <r[i]\)的全部區間\([x,y]\)的最小大值都爲i

因此這一部分區間咱們把它乘起來

而後全部區間最大值的和爲
\[ \large \sum_{i=1}^{n}(r[i]-i)\times(i-l[i])\times a[i] \]
最小值同理

而後求靠左/右的第一個比他大/小的數就能夠用單調棧來解決

最後把最大值的和和最小值的和相減就是答案

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define int long long
#define clear(x) memset(x,0,sizeof x)
const int maxn=1e5+5;
int read(){
    int s=0,f=1;char ch;
    while(!isdigit(ch=getchar()))(ch=='-')&&(f=-1);
    for(s=ch-'0';isdigit(ch=getchar());s=s*10+ch-'0');
    return s*f;
}
int a[maxn];
int s1[maxn],t1;
int l[maxn],r[maxn];
int n;
int ans=0;
inline void clearlr(){for(int i=1;i<=n;++i){l[i]=0;r[i]=n+1;}}
signed main(){
#ifndef nFILE
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
#endif
    int T=read();
    while(T--){
        ans=0;
        n=read();
        clear(a);
        for(int i=1;i<=n;++i){(a[i]=read());}
        clear(s1);t1=0;
        clearlr();
        for(int i=1;i<=n;++i){
            while(t1&&a[s1[t1]]<a[i])r[s1[t1--]]=i;
            s1[++t1]=i;
        }
        clear(s1);t1=0;
        for(int i=n;i;--i){
            while(t1&&a[s1[t1]]<a[i])l[s1[t1--]]=i;
            s1[++t1]=i;
        }
        for(int i=1;i<=n;++i){ans+=(r[i]-i)*(i-l[i])*a[i];}
        clear(s1);t1=0;
        clearlr();
        for(int i=1;i<=n;++i){
            while(t1&&a[s1[t1]]>a[i])r[s1[t1--]]=i;
            s1[++t1]=i;
        }
        clear(s1);t1=0;
        for(int i=n;i;--i){
            while(t1&&a[s1[t1]]>a[i])l[s1[t1--]]=i;
            s1[++t1]=i;
        }
        for(int i=1;i<=n;++i){ans-=(r[i]-i)*(i-l[i])*a[i];}
        cout<<ans<<endl;
    }
    return 0;
}
相關文章
相關標籤/搜索