Neat Tree
题意:
n个数,每个区间的贡献为区间内最大值减最小值,问这个n个数的总贡献是多少?也就是n所能组成的所有区间的贡献值之和
题解:
我们可以用单调栈来做
第i个数对答案的贡献值为=h[i] * 作为最大值出现的次数-h[i] * 作为最小值出现的次数
求每个点贡献值的过程可以用单调栈来维护
例如求最大值的次数:
对于第i点,找到左端第一个比h[i]大的数,记其位置为L,找到右端第一个比h[i]大的数,记其位置为R,(默认第0个位置最小,第n+1个位置最大)则区间(L,R)区间内包含点i的子区间个数就是(i-L) * (R-i)
答案的贡献就是h[i] * (i-L) * (R-i)
ll sum=0;stack<int>stk;for(int i=1;i<=n;i++)cin>>h[i];for(int i=1;i<+n;i++){while(!stk.empty()&&h[stk.top()]>=h[i])stk.pop();//记录左端第一个比h[i]小的index if(stk.empty())l[i]=1;//如果栈内无元素,取左端 else l[i]=stk.pop()+1;//stk.pop()是第一个比h[i]小的坐标,加一则是恰好不小于h[i]的 stk.push(i); }while(!stk.empty())stk.pop();//清空栈
代码:
#include<cstdio>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
const int maxn=1e6+10;
typedef long long ll;
int l[maxn], r[maxn];
ll h[maxn];int main(){int n;while(~scanf("%d", &n)){for(int i=1; i<=n; i++)scanf("%lld", &h[i]);stack<int> s;for(int i=1; i<=n; i++){while(!s.empty() && h[i]>=h[s.top()]) s.pop();//注意等号问题,因为区间不要重复。if(s.empty()) l[i]=0;else l[i]=s.top();s.push(i);}//--------------计算每个区间的最大值之和 while(!s.empty()) s.pop();for(int i=n; i>=1; i--){while(!s.empty() && h[i]>h[s.top()]) s.pop();if(s.empty()) r[i]=n+1;else r[i]=s.top();s.push(i);}ll maxx=0;for(int i=1; i<=n; i++)maxx+=h[i]*(i-l[i])*(r[i]-i);//----------计算每个区间的最小值之和 while(!s.empty()) s.pop();for(int i=1; i<=n; i++){while(!s.empty() && h[i]<=h[s.top()]) s.pop();if(s.empty()) l[i]=0;else l[i]=s.top();s.push(i);}while(!s.empty()) s.pop();for(int i=n; i>=1; i--){while(!s.empty() && h[i]<h[s.top()]) s.pop();if(s.empty()) r[i]=n+1;else r[i]=s.top();s.push(i);}ll minn=0;for(int i=1; i<=n; i++)minn+=h[i]*(i-l[i])*(r[i]-i);printf("%lld\n", maxx-minn);}return 0;
}