Description
给定一个长度为 \(n\) 的排列 \(a_1, a_2, \dots, a_n\)。
一个区间 \([l, r]\) 是锯齿形的,当且仅当该区间包含一个 21435 子序列;即存在整数 \(i_1, i_2, i_3, i_4, i_5\),满足 \(l \leq i_1 < i_2 < i_3 < i_4 < i_5 \leq r\),并且 \(a_{i_2} < a_{i_1} < a_{i_4} < a_{i_3} < a_{i_5}\)。
你的任务是计算所有 \(n(n+1)/2\) 个区间中有多少个是锯齿形的。
\(n\leq 10^6\)。
Solution
首先问题等价于对于每个 \(i\),求出 \(R_i\) 表示满足条件的以 \(i\) 为左端点的最小右端点。
考虑枚举 \(i_1\) 的值,那么 \(i_2\) 一定是 \(i_1\) 右边第一个值小于 \(a_{i_1}\) 的位置,所以只需要求 \([i_2+1,n]\) 的最短前缀,使得值大于 \(a_{i_1}\) 的所有位置存在形如 435 的子序列。
显然是需要枚举 435 中 3 的位置 \(k\)(即 \(i_4\)),求出所有以 \(k\) 为 \(i_4\) 的满足条件的极短区间 \([i_3,i_5]\)。
直观上看这样的极短区间可能很多,但是其实有用的只有 \(O(n)\) 个,且对于每个 \(i_4\) 只有至多一个!
考虑取 \(i_5\) 是 \(k\) 右边第一个值大于 \(a_{k}\) 的位置,那么 \(i_3\) 就唯一确定了。如果实际取得 \(i_5\) 不是 \(k\) 右边第一个大于 \(a_k\) 的位置,考虑分讨一下大小关系:

会发现这样的 \(i_5\) 没有任何意义。
找到这些极短的 \([i_3,i_5]\) 后按照值域从大到小进行扫描线即可。
时间复杂度:\(O(n\log n)\)。
Code
#include <bits/stdc++.h>// #define int int64_tconst int kMaxN = 1e6 + 5;int n;
int a[kMaxN], pos[kMaxN], nxt1[kMaxN], nxt2[kMaxN], r[kMaxN];
std::vector<std::pair<int, int>> vec[kMaxN];struct SGT {int N, mx[kMaxN * 4];void pushup(int x) { mx[x] = std::max(mx[x << 1], mx[x << 1 | 1]); }void build(int n) {for (N = 1; N <= n + 1; N <<= 1) {}std::fill_n(mx, 2 * N, -1e9);}void update(int x, int v) {x += N, mx[x] = std::max(mx[x], v);for (x >>= 1; x; x >>= 1) pushup(x);}int query(int l, int r) {if (l > r) return -1e9;int ret = -1e9;for (l += N - 1, r += N + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {if (~l & 1) ret = std::max(ret, mx[l ^ 1]);if (r & 1) ret = std::max(ret, mx[r ^ 1]);}return ret;}
} sgt1, sgt2;void getnxt1() {static int stk[kMaxN];int top = 0;stk[0] = n + 1;for (int i = n; i; --i) {for (; top && a[i] > a[stk[top]]; --top) {}nxt1[i] = stk[top], stk[++top] = i;}
}void getnxt2() {static int stk[kMaxN];int top = 0;stk[0] = n + 1;for (int i = n; i; --i) {for (; top && a[i] < a[stk[top]]; --top) {}nxt2[i] = stk[top], stk[++top] = i;}
}void dickdreamer() {std::cin >> n;for (int i = 1; i <= n + 1; ++i) r[i] = n + 1, vec[i].clear();for (int i = 1; i <= n; ++i) std::cin >> a[i], pos[a[i]] = i;getnxt1(), getnxt2();sgt1.build(n);for (int i = 1; i <= n; ++i) {if (nxt1[i] != n + 1) {int l = sgt1.query(a[i] + 1, a[nxt1[i]] - 1);if (l >= 1) vec[a[i]].emplace_back(l, nxt1[i]);}sgt1.update(a[i], i);}sgt2.build(n);for (int v = n; v; --v) {int i = pos[v];if (nxt2[i] != n + 1) {r[i] = std::min(r[i], -sgt2.query(nxt2[i] + 1, n));}for (auto [l, r] : vec[v]) sgt2.update(l, -r);}int64_t ans = 0;for (int i = n; i; --i) {r[i] = std::min(r[i], r[i + 1]);ans += n - r[i] + 1;}std::cout << ans << '\n';
}int32_t main() {
#ifdef ORZXKRfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);
#endifstd::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);int T = 1;std::cin >> T;while (T--) dickdreamer();// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";return 0;
}