我们考虑每个点成为区间左端点时合法的右区间范围,我们假定a是下标比i大的且值比i大的最小下标,b是下标比a大且值比i大的最小下标,那么以i为左端点的合法区间范围应是[a, b -1]。同理我们也能求出i是区间右端点时合法的区间[ql, qr]。set即可完成。
我们从左往右扫,每个点是左端点时的合法区间[l, r]应该在l插入,并且在r删除,扫到一个点i时,应询问在其合法区间[ql, qr]内有多少个合法左端点。树状数组即可完成。
复杂度O(nlog(n))
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <set>
#include <vector>
using namespace std;
const int N = 1e6 + 100;
int tree[N];
void insert(int id, int x) {
for (; id < N; id += id & -id) tree[id] += x;
}
int query(int id) {
int sum = 0;
for (; id > 0; id -= id & -id) sum += tree[id];
return sum;
}
int query(int l, int r) {
return query(r) - query(l - 1);
}
int n;
int sa[N], rev[N], ql[N], qr[N];
vector<int> Inc[N], Dec[N];
void solve() {
for (int i = 1; i <= n; i++) rev[sa[i]] = i;
set<int> S;
for (int i = 1; i <= n; i++) {
int id = rev[i];
S.insert(id);
auto a = S.upper_bound(id);
if (a == S.end()) continue;
Inc[*a].push_back(id);
a++;
if (a != S.end()) Dec[(*a) - 1].push_back(id);
}
S.clear();
for (int i = n; i >= 1; i--) {
int id = rev[i];
S.insert(-id);
auto a = S.upper_bound(-id);
if (a == S.end()) continue;
qr[id] -= (*a);
a++;
if (a == S.end()) ql[id] = 1;
else ql[id] -= (*a) - 1;
}
long long ans = 0;
for (int i = 1; i <= n; i++) {
for (int v : Inc[i]) insert(v, 1);
if (ql[i] && qr[i]) ans += query(ql[i], qr[i]);
for (int v : Dec[i]) insert(v, -1);
}
printf("%lld\n", ans);
}
int main() {
//freopen("0.txt", "r", stdin);
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", sa + i);
solve();
return 0;
}
京公网安备 11010502036488号