洛谷P6503[COCI2010-2011#3] DIFERENCIJA

[COCI2010-2011#3] DIFERENCIJA

题目描述

给出一个长度为 n n n 的序列 a i a_i ai,求出下列式子的值:

∑ i = 1 n ∑ j = i n max ⁡ i ≤ k ≤ j a k − min ⁡ i ≤ k ≤ j a k \sum_{i=1}^{n} \sum_{j=i}^{n} \max_{i\le k\le j} a_k-\min_{i\le k\le j} a_k i=1nj=inikjmaxakikjminak

即定义一个子序列的权值为序列内最大值与最小值的差。求出所有连续子序列的权值和。

输入格式

输入第一行一个整数 n n n,表示序列的长度。

接下来的 n n n 行,每行一个整数 a i a_i ai,描述这个序列。

输出格式

输出一行一个整数,表示式子的答案。

样例 #1

样例输入 #1

3
1
2
3

样例输出 #1

4

样例 #2

样例输入 #2

4
7
5
7
5

样例输出 #2

12

样例 #3

样例输入 #3

4
3
1
7
2

样例输出 #3

31

提示

数据规模与约定

对于 100 % 100\% 100% 的数据,保证 2 ≤ n ≤ 3 × 1 0 5 2\le n\le 3\times 10^5 2n3×105 1 ≤ a i ≤ 1 0 8 1\le a_i\le 10^8 1ai108

说明

题目译自 COCI2010-2011 CONTEST #3 T5 DIFERENCIJA

偷偷打开标签

 单调队列 \colorbox{darkblue}{\color{darkblue}\boxed{\begin{gathered}\begin{gathered}\small \color{white}{\text{ 单调队列}}\end{gathered}\cr\end{gathered}}}  单调队列  栈 \colorbox{darkblue}{\color{darkblue}\boxed{\begin{gathered}\begin{gathered}\small \color{white}{\text{ 栈}}\end{gathered}\cr\end{gathered}}}    COCI \colorbox{skyblue}{\color{skyblue}\boxed{\begin{gathered}\begin{gathered}\small \color{white}{\text{ COCI}}\end{gathered}\cr\end{gathered}}}  COCI  O2优化 \colorbox{orange}{\color{orange}\boxed{\begin{gathered}\begin{gathered}\small \color{white}{\text{ O2优化}}\end{gathered}\cr\end{gathered}}}  O2优化  2010 \colorbox{blue}{\color{blue}\boxed{\begin{gathered}\begin{gathered}\small \color{white}{\text{ 2010}}\end{gathered}\cr\end{gathered}}}  2010

那么,我们从哪方面入手?

How about 单调队列?

似乎麻烦,我不干

那栈呢

就试试看吧
先把他当最大的,然后最小
最大左边缘lmx 最大右边缘 rmx 最小左边缘 lmn 最小右边缘 rmn
然鹅,有一个大问题,画个图就知道

7 4 5 3 7
7-------》
《------7

有没有发现,两个7行走的路线有重复?

于是呢,我便发明了个好方法,就是左(右)区间可以右等于的,另一边不行
然后呢,样例没过

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a[300007];
struct pd{
    int lmx,rmx,lmn,rmn;
}k[300007];
struct _stack_{
    int a[1000007],n;
    void push(int x){a[++n]=x;return;}
    void pop(){--n;return;}
    void clear(){n=0;}
    bool empty(){return (n==0);};
    int size(){return n;}
    int top(){return a[n];}
};
_stack_ lmx,rmx,lmn,rmn;
int ans;
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",a+i);
    for(int i=1;i<=n;i++){
        while(!lmx.empty()&&a[lmx.top()]<=a[i])
            lmx.pop();
        k[i].lmx=(!lmx.empty())?(lmx.top()):1;
        lmx.push(i);
    }
    for(int i=n;i>=1;i--){
        while(!rmx.empty()&&a[rmx.top()]<a[i])
            rmx.pop();
        k[i].rmx=(!rmx.empty())?(rmx.top()):n;
        rmx.push(i);
    }
    for(int i=1;i<=n;i++){
        while(!lmn.empty()&&a[lmn.top()]>=a[i])
            lmn.pop();
        k[i].lmn=(!lmn.empty())?(lmn.top()):1;
        lmn.push(i);
    }
    for(int i=n;i>=1;i--){
        while(!rmn.empty()&&a[rmn.top()]>a[i])
            rmn.pop();
        k[i].rmn=(!rmn.empty())?(rmn.top()):n;
        rmn.push(i);
    }
    for(int i=1;i<=n;i++){
    	ans+=a[i]*((k[i].rmx-k[i].lmx+1)-(k[i].rmn-k[i].lmn+1));
	}
    printf("%lld",ans);
    return 0;
}

经过一番修改,我发现了如下的问题:

  1. 左右区间不合法的也判进去了
  2. 少了很多区间
    修改后的样子:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a[300007];
struct pd{
    int lmx,rmx,lmn,rmn;
}k[300007];
struct _stack_{//手写栈
    int a[1000007],n;
    void push(int x){a[++n]=x;return;}
    void pop(){--n;return;}
    void clear(){n=0;}
    bool empty(){return (n==0);};
    int size(){return n;}
    int top(){return a[n];}
};
_stack_ lmx,rmx,lmn,rmn;
int ans;
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",a+i);
    for(int i=1;i<=n;i++){
        while(!lmx.empty()&&a[lmx.top()]<=a[i])
            lmx.pop();
        k[i].lmx=(!lmx.empty())?(lmx.top()+1):1;
        lmx.push(i);
    }
    for(int i=n;i>=1;i--){
        while(!rmx.empty()&&a[rmx.top()]<a[i])
            rmx.pop();
        k[i].rmx=(!rmx.empty())?(rmx.top()-1):n;
        rmx.push(i);
    }
    for(int i=1;i<=n;i++){
        while(!lmn.empty()&&a[lmn.top()]>=a[i])
            lmn.pop();
        k[i].lmn=(!lmn.empty())?(lmn.top()+1):1;
        lmn.push(i);
    }
    for(int i=n;i>=1;i--){
        while(!rmn.empty()&&a[rmn.top()]>a[i])
            rmn.pop();
        k[i].rmn=(!rmn.empty())?(rmn.top()-1):n;
        rmn.push(i);
    }
    for(int i=1;i<=n;i++){
    	ans+=a[i]*((k[i].rmx-i+1)*(i-k[i].lmx+1)-(k[i].rmn-i+1)*(i-k[i].lmn+1));
	}
    printf("%lld",ans);
    return 0;
}

完美  AC \colorbox{green}{\color{green}\boxed{\begin{gathered}\begin{gathered}\small \color{white}{\text{ AC}}\end{gathered}\cr\end{gathered}}}  AC

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值