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;
}