JYOJ 1859 子序列累加和 单调栈

title

JYOJ 1859

Description

小x 在学习数列。他想到一个数学问题: 现在有 \(N\) 个数的数列。现在你定义一个子序列是数列的连续一部分,子序列的值是这个子序列中最大值和最小值之差。 给你这 \(N\) 个数,小x 想知道所有子序列的值得累加和是多少。

Input Format

第一行一个整数 \(N(2\leqslant N\leqslant 3e5)\) 接下来 \(N\) 行,每行一个整数 \(A_i(0<A_i\leqslant 1e8)\),表示数列的值。

Output Format

一个整数,所求的子序列值得累加和。

analysis

这题还真是一个答案计数题。

我们只需要搞清楚每个子区间的最值之差即可,但是复杂度不允许,我们需要用数据结构来维护一下。

我们规定:\(Max[i]\) 表示第 \(i\) 个数值是多少区间的最大值,\(Min[i]\) 表示第 \(i\) 个数值是多少区间的最小值。

那么答案就是 \(\sum Max[i]*a[i]-\sum Min[i]*a[i]\)

不过怎样得到 \(Max[i]\)\(Min[i]\) 呢?

我们只需要用四个单调栈来维护,分别找到这个数向左有多少个区间是最值,向右有多少个区间是最值就好了。

最后用上述公式统计即可。

code

#include<bits/stdc++.h>

typedef long long ll;
const int maxn=3e5+10;
typedef ll larr[maxn];

namespace IO
{
    char buf[1<<15],*fs,*ft;
    inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1, ch=getc();
        while (!isdigit(ch) && ch^'-') ch=getc();
        if (ch=='-') f=-1, ch=getc();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getc();
        x*=f;
    }

    char Out[1<<24],*fe=Out;
    inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
    template<typename T>inline void write(T x,char str)
    {
        if (!x) *fe++=48;
        if (x<0) *fe++='-', x=-x;
        T num=0, ch[20];
        while (x) ch[++num]=x%10+48, x/=10;
        while (num) *fe++=ch[num--];
        *fe++=str;
    }
}

using IO::read;
using IO::write;

ll top1,top2,top3,top4,ans;
larr s1,s2,s3,s4,a;
larr sum1,sum2,sum3,sum4;
int main()
{
    ll n;read(n);
    for (ll i=1; i<=n; ++i) read(a[i]);
    s1[++top1]=1, s2[++top2]=n;
    s3[++top3]=1, s4[++top4]=n;
    for (ll i=2; i<=n; ++i)//正序 最小
    {
        while (a[i]<a[s1[top1]] && top1) sum1[i]+=sum1[s1[top1]]+1, --top1;
        s1[++top1]=i;
    }
    for (ll i=n-1; i>=1; --i)//逆叙 最小
    {
        while (a[i]<=a[s2[top2]] && top2) sum2[i]+=sum2[s2[top2]]+1, --top2;
        s2[++top2]=i;
    }
    for (ll i=2; i<=n; ++i)//正序 最大
    {
        while (a[i]>a[s3[top3]] && top3) sum3[i]+=sum3[s3[top3]]+1, --top3;
        s3[++top3]=i;
    }
    for (ll i=n-1; i>=1; --i)//逆序 最大
    {
        while (a[i]>=a[s4[top4]] && top4) sum4[i]+=sum4[s4[top4]]+1, --top4;
        s4[++top4]=i;
    }
    for (ll i=1; i<=n; ++i) ans+=((sum3[i]+1)*(sum4[i]+1)-(sum1[i]+1)*(sum2[i]+1))*a[i];
    write(ans,'\n');
    IO::flush();
    return 0;
}

转载于:https://www.cnblogs.com/G-hsm/p/11424519.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值