牛客 Neat Tree (单调栈)
https://ac.nowcoder.com/acm/problem/15815
首先暴力枚举每一个区间必定是超时的。那么考虑每个点对于答案的贡献值,可以这样想,对于点h[i]作为最大值在多少个区间出现,作为最小值在多少个区间出现?这个点对于答案的贡献就是h[i]*作为最大值出现的次数 - h[i]*作为最小值出现的次数,对于每个点,求一下贡献累加即可。
求每个点贡献的过程用单调栈来维护,拿求最大值的次数来举例。对于点i,找到左端第一个比h[i]大的数,记其位置为L,找到右端第一个比h[i]大的数,记其位置为R,则在(L,R)区间内包含点[i]的子区间个数就是 (i-L+1)*(R-i+1),可以画图理解一下。那么对于答案的贡献就是h[i]*(i-L+1)*(R-i+1)。
同理求出最小值的贡献,减去这个贡献,最终得到答案
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 1e6+5; 5 int h[maxn],l[maxn],r[maxn],n; 6 void solve(){ 7 ll sum = 0; 8 stack<int> stk; 9 for(int i = 1;i<=n;i++) cin>>h[i]; 10 //先求h[i]作为一段区间的最小值出现的次数 11 for(int i = 1;i<=n;i++) { 12 while(!stk.empty() && h[stk.top()]>=h[i]) stk.pop(); 13 if(stk.empty()) l[i] = 1; 14 else l[i] = stk.top() + 1;//记录h[i]左端第一个比h[i]小的index; 15 stk.push(i);//压入栈中 16 } 17 while(!stk.empty()) stk.pop(); 18 for(int i = n;i>=1;i--){ 19 while(!stk.empty() && h[stk.top()]>h[i]) stk.pop(); 20 if(stk.empty()) r[i] = n; 21 else r[i] = stk.top()-1;//记录h[i]右端第一个比h[i]小的index; 22 stk.push(i); 23 } 24 while(!stk.empty()) stk.pop(); 25 // 26 for(int i = 1;i<=n;i++) sum-=1ll*h[i]*(i-l[i]+1)*(r[i]-i+1); 27 //再求h[i]作为一段区间的最小值出现的次数 28 for(int i = 1;i<=n;i++){ 29 while(!stk.empty() && h[stk.top()]<=h[i]) stk.pop(); 30 if(stk.empty()) l[i] = 1; 31 else l[i] = stk.top() + 1;//记录h[i]左端第一个比h[i]大的数的index; 32 stk.push(i); 33 } 34 while(!stk.empty()) stk.pop(); 35 for(int i = n;i>=1;i--){ 36 while(!stk.empty() && h[stk.top()]<h[i]) stk.pop(); 37 if(stk.empty()) r[i] = n; 38 else r[i] = stk.top()-1;//记录h[i]右端第一个比h[i]大的数的index; 39 stk.push(i); 40 } 41 for(int i = 1;i<=n;i++) sum+=1ll*h[i]*(i-l[i]+1)*(r[i]-i+1); 42 cout<<sum<<endl; 43 } 44 int main(){ 45 while(cin>>n){ 46 solve(); 47 } 48 return 0; 49 }