T706110 学习小组

题意简述

给定长度为 n n n2×105 n \leq 2 \times 10^5 )的序列 s s ,统计满足下标严格递增且

si<sj<sksi>sj>sk s_i < s_j < s_k \quad \text{或} \quad s_i > s_j > s_k

的三元组 (i,j,k) (i, j, k) 的数量。

算法思路

将三元组问题转化为“中间人”问题:

对于每个位置 j j ,统计:

  • 前面比 sj s_j 小的数量:preLess[j] \mathrm{preLess}[j]
  • 前面比 sj s_j 大的数量:preGreater[j] \mathrm{preGreater}[j]
  • 后面比 sj s_j 小的数量:postLess[j] \mathrm{postLess}[j]
  • 后面比 sj s_j 大的数量:postGreater[j] \mathrm{postGreater}[j]

则每个位置 j j 的贡献为:

preLess[j]×postGreater[j]+preGreater[j]×postLess[j] \mathrm{preLess}[j] \times \mathrm{postGreater}[j] + \mathrm{preGreater}[j] \times \mathrm{postLess}[j]

总答案为所有位置贡献之和。

实现方法

  1. 离散化:将值域映射到连续区间,方便树状数组处理。
  2. 树状数组:两次扫描(正序 + 倒序)统计前后关系。
  3. 累加贡献:线性统计每个位置的贡献。

复杂度分析

  • 时间复杂度O(nlogn) O(n \log n)
  • 空间复杂度O(n) O(n)

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int N = 2e5 + 10;

int n, s[N], tmp[N], m;
ll preL[N], preG[N], postL[N], postG[N], ans;
int c[N];

inline void add(int x, int v = 1) {
for (; x <= m; x += x & -x) c[x] += v;
}

inline int ask(int x) {
int s = 0;
for (; x; x -= x & -x) s += c[x];
return s;
}

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> s[i];
tmp[i] = s[i];
}

// 离散化
sort(tmp + 1, tmp + n + 1);
m = unique(tmp + 1, tmp + n + 1) - tmp - 1;

// 正序
for (int i = 1; i <= n; ++i) {
int r = lower_bound(tmp + 1, tmp + m + 1, s[i]) - tmp;
preL[i] = ask(r - 1);
preG[i] = ask(m) - ask(r);
add(r);
}

// 倒序
memset(c, 0, sizeof(c));
for (int i = n; i >= 1; --i) {
int r = lower_bound(tmp + 1, tmp + m + 1, s[i]) - tmp;
postL[i] = ask(r - 1);
postG[i] = ask(m) - ask(r);
add(r);
}

// 累加答案
for (int i = 1; i <= n; ++i)
ans += preL[i] * postG[i] + preG[i] * postL[i];
cout << ans << '\n';
return 0;
}

提示

  • 树状数组常数较小,可直接通过。
  • 注意离散化时处理重复值。