我们考虑每个点成为区间左端点时合法的右区间范围,我们假定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; }