牛客竞赛Neat Tree(单调栈)

It’s universally acknowledged that there’re innumerable trees in the campus of HUST.
There is a row of trees along the East-9 Road which consists of N trees. Now that we know the height of each tree, the gardeners of HUST want to know the super-neatness of this row of trees. The neatness of a sequence of trees is defined as the difference between the maximum height and minimum height in this sequence. The super-neatness of this sequence of trees is defined as the sum of neatness of all continous subsequences of the trees.
Multiple cases please process until the end of input. There are at most 100 test cases.

题目链接
参考题解1
参考题解2
一个单调栈题目,题意是给你一排树的高度(一个序列),求这个序列所有子区间中的最大值和最小值的加和。
刚开始一点思路没有,实在想不到跟单调栈怎么扯上的关系,于是翻了两篇题解。大致是这样的想法:我们求子区间中最大值和最小值的差值,那么一个最小(大)值可能在多个区间中出现,那么我们就看一下这个最小(大)值影响的范围就好了。例如,如果是找最小值,那么就维护一个单调增的单调栈,因为我们要找左边或者右边第一个小于(或者小于等于,这个一会再说)它得数,因为从那个数到本身才是自己能够影响的范围,右侧也是一样,那么就需要求一下这个数字能影响的子区间的数目了:首先我们先把刚刚求出来的左右端点构成的一个大区间用当前数值分成左右两部分,那么只求一侧的子区间的个数很简单了,就是这一侧加上自身的数字个数,那么总的就是左右两侧子区间的乘积减一,因为这两侧的子区间可以自由组合,但是要减一,减去的这个数字自身构成的区间。只有一个数字的区间是没有价值的,因为最大值最小值都是自身,一减就变0了。还有就是有可能遇到有与自身相同的元素怎么办呢,我们可以把这个问题想成数学中的区间,左开右闭之类的,将左边的算入区间右边的去掉,或者左边的去掉右边的算上,都可以。否则如果两侧的单调性完全一致会出问题,重复或者缺少了。可以想一下,假如你右侧有一个一样的数,你单纯的两侧都算上,你在遍历到这个的时候没有问题,但是你遍历到右侧这个和之前一样的元素的话,那么左侧相应也要把刚刚的那个再算一次,这样就出现了重复,另一种情况也是一样的道理。这样整个算法就结束了。下面是代码(再次感谢两位的题解博客):

#include <cstdio>
#include <iostream>
using namespace std;
typedef long long LL;
const int maxn = 1e6+5;
int _left[maxn], _right[maxn], number[maxn], _stack[maxn];

int main()
{
    int n;
    while(~scanf("%d", &n))
    {
        LL ans = 0;
        int top = 0;
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &number[i]);
            while(top > 0 && number[_stack[top]] > number[i])  top--;
            _left[i] = (0 == top) ? 0 : _stack[top];
            _stack[++top] = i;
        }
        top = 0;
        for(int i = n; i >= 1; i--)
        {
            while(top > 0 && number[_stack[top]] >= number[i])  top--;
            _right[i] = (0 == top) ? n+1 : _stack[top];
            _stack[++top] = i;
        }
        for(int i = 1; i <= n; i++)
            ans -= (1LL*number[i])*(LL(_right[i]-i)*(i-_left[i])-1);
        top = 0;
        for(int i = 1; i <= n; i++)
        {
            while(top > 0 && number[_stack[top]] <= number[i])  top--;
            _left[i] = (0 == top) ? 0 : _stack[top];
            _stack[++top] = i;
        }
        top = 0;
        for(int i = n; i >= 1; i--)
        {
            while(top > 0 && number[_stack[top]] < number[i])  top--;
            _right[i] = (0 == top) ? n+1 : _stack[top];
            _stack[++top] = i;
        }
        for(int i = 1; i <= n; i++)
            ans += (1LL*number[i])*(LL(_right[i]-i)*(i-_left[i])-1);
        printf("%lld\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_40788897/article/details/97023943