tyvj4875:排列(单调栈)

既然是让我两天都想不明白的题,就写总结吧。
题面
题意:给你一个n个数的随机排列,设ans[k]为区间内(最大值-最小值)≤k的区间个数。
求ans[0~n-1]。

暴力就是固定左端点,右端点不断向右拓展,用得到的新数来更新当前最大值与最小值。

实际操作中,我们发现很多次拓展都不能更新最大值与最小值。

就萌发出一个玄学优化:每次直接跳到能更新最大值或最小值的点,然后统计答案。
这用两个单调栈即可预处理出每个点右边第一个比它大(小)的数。

然后据说由于这是一个随机排列,有证明这样就是O(nlogn)的。

#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))

typedef long long LL;

const int N=100100;

int T,n,a[N];
int big[N],small[N];
int st1[N],l1,st2[N],l2;
long long ans[N];

int main()
{
    cin>>T;
    while(T--)
    {
        l1=l2=0;
        mmst(ans,0);

    cin>>n;
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);

    st1[0]=st2[0]=n+1;

    for(int i=n;i>=1;i--)
    {
        while(l1&&a[st1[l1]]<=a[i])
        l1--;
        while(l2&&a[st2[l2]]>=a[i])
        l2--;
        big[i]=st1[l1];
        small[i]=st2[l2];
        st1[++l1]=i;
        st2[++l2]=i;
    }

    for(int i=1;i<=n;i++)
    {
        int p=i,s=i,b=i;
        while(p<=n)
        {
            int ne;
            if(small[s]<big[b])
            {
                ne=small[s];
                ans[a[b]-a[s]]+=ne-p;
                p=s=ne;
            }
            else
            {
                ne=big[b];
                ans[a[b]-a[s]]+=ne-p;

                p=b=ne;
            }
        }
    }
    cout<<n<<endl;
    ans[0]=n;
    for(int i=1;i<n;i++)
    {
        ans[i]+=ans[i-1];
        printf("%lld\n",ans[i]);
    }
    }

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值