[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=1∑nj=i∑ni≤k≤jmaxak−i≤k≤jminak
即定义一个子序列的权值为序列内最大值与最小值的差。求出所有连续子序列的权值和。
输入格式
输入第一行一个整数 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 2≤n≤3×105, 1 ≤ a i ≤ 1 0 8 1\le a_i\le 10^8 1≤ai≤108。
说明
题目译自 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;
}
经过一番修改,我发现了如下的问题:
- 左右区间不合法的也判进去了
- 少了很多区间
修改后的样子:
#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