牛客网 223C 区区区间间间(单调栈)
题目链接:区区区间间间
题意:给出长度为n的数字序列ai,定义区间(l,r)的价值为,
请你计算出。
题解:单调栈求ai左边和右边第一个比它小的位置,需要减去ai的个数为$(R_i-i+1)*(i-L_i+1)-1$。同理再用单调栈求ai左边和右边第一个比它大的位置,加上需要加上的ai个数即可。
解释1:需要减去的ai个数为$(R_i-i+1)*(i-L_i+1)-1$。
举个例子:1 2 3 4 5,求必须包含3的区间个数,左边有3种选择:1 2;2;不选;,右边也有三种选择:4 5;4;不选;但是题目中要求区间长度至少为2,所以两边都不选的情况不能计算在内。
解释2:为什么单调栈中一个a[i]<=a[st.top()],另一个是a[i]<a[st.top()](>=和>也同理)。
举个例子:5 6 5。这种情况很明显只有三个区间[5 6],[5 6 5],[6 5],即减去15。
但是如果直接用<=,那么每个位置对应的区间(li,ri)分别为[1,3],[2,2],[1,3]。减去20。可以发现[1,3]区间被减了两次,所以需要保证相等的时候一端扩展,避免重复计算。
stack:
1 #include <stack> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 8 const int N=1e5+10; 9 typedef long long ll; 10 stack <int> st; 11 ll l[N],r[N],a[N]; 12 13 int main(){ 14 int t; 15 scanf("%d",&t); 16 17 while(t--){ 18 int n; 19 ll sum=0; 20 scanf("%d",&n); 21 for(int i=1;i<=n;i++) scanf("%lld",&a[i]); 22 for(int i=1;i<=n;i++){ 23 while(st.size()&&a[i]<=a[st.top()]) st.pop(); 24 l[i]=st.size()==0?1:st.top()+1; 25 st.push(i); 26 } 27 while(st.size()) st.pop(); 28 for(int i=n;i>=1;i--){ 29 while(st.size()&&a[i]<a[st.top()]) st.pop(); 30 r[i]=st.size()==0?n:st.top()-1; 31 st.push(i); 32 } 33 while(st.size()) st.pop(); 34 for(int i=1;i<=n;i++) sum-=((r[i]-i+1)*(i-l[i]+1)-1)*a[i]; 35 for(int i=1;i<=n;i++){ 36 while(st.size()&&a[i]>=a[st.top()]) st.pop(); 37 l[i]=st.size()==0?1:st.top()+1; 38 st.push(i); 39 } 40 while(st.size()) st.pop(); 41 for(int i=n;i>=1;i--){ 42 while(st.size()&&a[i]>a[st.top()]) st.pop(); 43 r[i]=st.size()==0?n:st.top()-1; 44 st.push(i); 45 } 46 while(st.size()) st.pop(); 47 for(int i=1;i<=n;i++) sum+=((r[i]-i+1)*(i-l[i]+1)-1)*a[i]; 48 printf("%lld\n",sum); 49 } 50 51 return 0; 52 }