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)
代码:
#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; }