Neat Tree
Neat Tree
https://ac.nowcoder.com/acm/problem/15815
单调栈
单调栈接触挺久了,一直没有仔细研究。
昨天模拟赛,一道单调栈没有看出来。
现在认真学学!
对于这道题,因为它是连续子序列
所以我们可以统计每一个值的贡献
即,(作为最大值的次数-作为最小值的次数)*高度
如何求作为最大值的次数?
我们可以求解,向左走第一个比他大的索引,向右走第一个比他大的索引
那么,在这个区间内。他就是最大值!
利用单调栈可以很容易地统计出来!
然后我们可以用同样的方法统计出最小值的次数
但是哇,我们这样有一个bug
就是,这个区间内有重复的数,我们统计时可能会有区间被重复选取!
那么该怎么办呢?
学到了!
我们可以在单调栈判断时,对于左边我们取等,对于右边我们不取等!
真的巧妙!
#include<iostream> #include<algorithm> #include<vector> using namespace std; typedef long long ll; const int max_n = 1e6+100; const int inf = 1e9; int h[max_n],lft[max_n],rgt[max_n]; int n; int main(){ ios::sync_with_stdio(0); while (cin>>n){ for (int i=1;i<=n;++i)cin>>h[i]; vector<int> stack; stack.push_back(0); h[n+1]=h[0]=inf; for (int i=1;i<=n;++i){ while (h[stack.back()]<=h[i])stack.pop_back(); lft[i]=i-stack.back(); stack.push_back(i); } stack.clear(); stack.push_back(n+1); for (int i=n;i>=1;--i){ while (h[stack.back()]<h[i])stack.pop_back(); rgt[i]=stack.back()-i; stack.push_back(i); } ll ans = 0; for (int i=1;i<=n;++i)ans+=1LL*lft[i]*1LL*rgt[i]*1LL*h[i]; stack.clear(); h[0]=h[n+1]=0; stack.push_back(0); for (int i=1;i<=n;++i){ while (h[stack.back()]>=h[i])stack.pop_back(); lft[i]=i-stack.back(); stack.push_back(i); } stack.clear(); stack.push_back(n+1); for (int i=n;i>=1;--i){ while (h[stack.back()]>h[i])stack.pop_back(); rgt[i]=stack.back()-i; stack.push_back(i); } for (int i=1;i<=n;++i)ans-=1LL*lft[i]*1LL*rgt[i]*1LL*h[i]; cout<<ans<<endl; } }