做题感想
不得不感慨代码源 d i v . 1 div.1 div.1真的是大佬的世界,第一天每日一题就已经冲到了 c o d e f o r c e s 1900 codeforces\ 1900 codeforces 1900的水平了,不过通过这题学习了一下单调栈的运用我觉得倒也不错。
题意
给出一个数组求出所有子区间最大值和最小值的差值的和。
题解
这里我们将最大值和最小值分开看,所有子区间最大值和最小值差值的和我们也能够认为是所有子区间最大值的和和所有子区间最小值的和的差值,而子区间的最值必定是数组中的某一个数字并且它作为极值的区间必定是连续的。
换句话说我们只要找到了一个值作为极值的区间长度然后通过一系列四则运算就能得出答案了。极值的区间长度可以通过单调栈来维护。
-
单调栈
因为之前学过单调队列所以这次学单调栈还是比较容易的,单调队列是两个端点都能进出,单调栈就是只有一个端点能够进出。假设单调栈维护最小值我们只用将当前值与栈顶元素比较大小如果比栈顶元素小,栈顶元素弹出,代表该元素不可能成为接下来的最小值。上述操作结束后剩下的栈顶元素一定比当前元素小,之前的元素都比栈顶元素大,那我们就能够得出当前位置到栈顶元素所在位置前一位,当前元素就是极值。
-
相等的情况
因为我们之前还有一种情况就是有一大串元素都相等,这种情况下我们就要固定一个端点,让另一个端点向外延伸。
换句话说就是当前一个方向允许区间内存在等于的值另一个不存在,不然就会出现重复计算的情况。不理解的同学可以看一下 2 2 2 2 2\ 2\ 2\ 2 2 2 2 2这组数据。
void MAIN(){
cin>>n;
for(int i=1;i<=n;i++) cin>>num[i];
for(int i=1;i<=n;i++){//枚举左边界
while(!st1.empty()&&num[st1.top()]<=num[i]) st1.pop();//maxinum
while(!st2.empty()&&num[st2.top()]>=num[i]) st2.pop();//mininum
lmax[i]=st1.empty()?1:st1.top()+1;
lmin[i]=st2.empty()?1:st2.top()+1;
st1.push(i);st2.push(i);
}
while(!st1.empty()) st1.pop();
while(!st2.empty()) st2.pop();
for(int i=n;i>=1;i--){
while(!st1.empty()&&num[st1.top()]<num[i]) st1.pop();//maxinum
while(!st2.empty()&&num[st2.top()]>num[i]) st2.pop();//mininum
rmax[i]=st1.empty()?n:st1.top()-1;
rmin[i]=st2.empty()?n:st2.top()-1;
st1.push(i);st2.push(i);
}
int ans=0;
for(int i=1;i<=n;i++){
ans+=num[i]*(rmax[i]-i+1)*(i-lmax[i]+1);
ans-=num[i]*(rmin[i]-i+1)*(i-lmin[i]+1);
}
cout<<ans<<endl;
return ;
}