链接:https://ac.nowcoder.com/acm/problem/20806
来源:牛客网
题目描述
给出长度为n的序列a,其中第i个元素为a[i]
区间[l,r]的价值为:
要求计算:
输入描述:
第一行输入数据组数T
对于每组数据,第一行为一个整数n,表示序列长度
接下来一行有n个数,表示序列内的元素
输出描述:
对于每组数据,输出一个整数表示答案
示例1
输入
3
3
4 2 3
5
1 8 4 3 9
20
2 8 15 1 10 5 19 19 3 5 6 6 2 8 2 12 16 3 8 17
输出
5
57
2712
说明
对于一组测试数据的解释:
区间[1, 2]的贡献为:4 - 2 = 2
区间[1, 3]的贡献为:4 - 2 = 2
区间[2, 3]的贡献为:3 - 2 = 1
2 + 1 + 2 = 5.
备注:
T<=20,n<=1e5,a[i]<=1e5
思路:
问题转化为:
首先要发现题目式子里面的区间长度至少为2
利用单调栈求出每个数:
1. 作为最大值的左右拓展范围 l[i],r[i]
a[i]有贡献的区间有三种情况:
1. 左端点为l[i]到i-1 任意一点,右端点为i
2. 左端点为i,右端点为i+1 到r[i]任意一点
3. 左端点为l[i]到 i-1 任意一点, 右端点为i+1 到r[i]任意一点
总区间数 num=(r[i]-i+1-1)+(i-l[i]+1-1)+(r[i]-i+1-1)*(i-l[i]+1-1);
答案累加a[i]*num;
2. 作为最小值的左右拓展范围
最小值同上,减去就行了
还有一个坑点就是如果数组中相邻元素重复的情况会导致某些数值重复计算,为了防止重复,
求左右拓展范围的时候第一遍扫不能带等号,第二遍扫带等号(或者第一遍带等号,第二遍不带)
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
using namespace std;
#define int long long
const int inf=1e9;
const int maxm=1e5+5;
int a[maxm];
int l[maxm],r[maxm];
stack<int>stk;
int ans;
int n;
void getma(){//求a[i]为最大值的拓展范围
a[0]=a[n+1]=inf;//保证全部元素出栈
while(!stk.empty())stk.pop();
for(int i=1;i<=n+1;i++){//右
while(!stk.empty()&&a[stk.top()]<a[i]){
r[stk.top()]=i-1;
stk.pop();
}
stk.push(i);
}
while(!stk.empty())stk.pop();
for(int i=n;i>=0;i--){//左
while(!stk.empty()&&a[stk.top()]<=a[i]){
l[stk.top()]=i+1;
stk.pop();
}
stk.push(i);
}
for(int i=1;i<=n;i++){
int temp=(r[i]-i+1-1)+(i-l[i]+1-1)+(r[i]-i+1-1)*(i-l[i]+1-1);
ans+=temp*a[i];
}
}
void getmi(){//求a[i]为最小值的拓展范围
a[0]=a[n+1]=-1;//保证全部元素出栈
while(!stk.empty())stk.pop();
for(int i=1;i<=n+1;i++){//右
while(!stk.empty()&&a[stk.top()]>a[i]){
r[stk.top()]=i-1;
stk.pop();
}
stk.push(i);
}
while(!stk.empty())stk.pop();
for(int i=n;i>=0;i--){//左
while(!stk.empty()&&a[stk.top()]>=a[i]){
l[stk.top()]=i+1;
stk.pop();
}
stk.push(i);
}
for(int i=1;i<=n;i++){
int temp=(r[i]-i+1-1)+(i-l[i]+1-1)+(r[i]-i+1-1)*(i-l[i]+1-1);
ans-=temp*a[i];
}
}
signed main(){
int T;
cin>>T;
while(T--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
ans=0;
getma();
getmi();
cout<<ans<<endl;
}
return 0;
}
//https://ac.nowcoder.com/acm/problem/20806