题目描述
在绍兴市编程达人的聚会中,厌倦了求最大值的新昌小伙伴想出了一个求次大值的方案,想来考考大家。
给定一个 1 到 n 的数字各出现一次的排列 a[1]、a[2]、…、a[n],定义 f(l,r)表示 a[l]、a[l+1]、a[l+2]、…、a[r]中的次大值,你需要求出对于所有的 1<=i<j<=n,f(i,j)的和。
输入
第一行一个整数 n,第二行 n 个整数表示 a[i]。
输出
一行一个整数,表示答案。
样例
样例输入
3
2 3 1
样例输出
5
提示
样例输入2:
8
8 2 7 3 4 5 6 1
样例输出2:
136
【样例解释】
样例 1:区间[1,2]和[1,3]的次大值是 2,区间[2,3]的次大值是 1,求和后的结果 为 5。
【数据范围约定】
对于 30%的数据,n<=100;
对于 70%的数据,n<=5000;
对于 100%的数据,n<=100000。
分析
对于某个数x,考虑其作为次大值的区间
找出每个数左边第一个和第二个比它大的,右边同理,区间数即为 d1 * d3 + d2 * d4
实现
用双链表指示左右的大小关系,从小到大排序,枚举,求出贡献后将当前点删除。
for(int i = 1;i <= n;i++) l[i] = i - 1,r[i] = i + 1;
r[n + 1] = n + 1;
for(int i = 1;i <= n;i++){
int idx = a[i].second;
// 对应操作
r[l[idx]] = r[idx];
l[r[idx]] = l[idx];
}
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int N = 100000 + 10;
int n;
PII a[N];
int l[N],r[N];
LL res;
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin >> n;
for(int i = 1;i <= n;i++){
cin >> a[i].first;
a[i].second = i;
}
sort(a + 1,a + 1 + n);
for(int i = 1;i <= n;i++) l[i] = i - 1,r[i] = i + 1;
r[n + 1] = n + 1;
for(int i = 1;i <= n;i++){
int idx = a[i].second;
int l1,l2,r1,r2;
l1 = l[idx],l2 = l[l1],r1 = r[idx],r2 = r[r1];
res += (LL)i * ((l1 - l2) * (r1 - idx) + (r2 - r1) * (idx - l1));
r[l[idx]] = r[idx];
l[r[idx]] = l[idx];
}
cout << res;
return 0;
}