P1637 三元上升子序列

考点

  • 线段树
  • 树状数组

题解

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
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e4 + 50;
int n, tot, dis[maxn], a[maxn], bit[maxn];
int l[maxn], r[maxn];

int lowbit(int x) { return x & -x; }

void add(int x, int v) {
while (x <= n) {
bit[x] += v;
x += lowbit(x);
}
}

int ask(int x) {
int s = 0;
while (x > 0) {
s += bit[x];
x -= lowbit(x);
}
return s;
}

int main() {
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i], dis[i] = a[i];
sort(dis + 1, dis + 1 + n);
tot = unique(dis + 1, dis + 1 + n) - 1 - dis;
for (int i = 1; i <= n; ++i) {
int x = lower_bound(dis + 1, dis + 1 + tot, a[i]) - dis;
a[i] = x;
}
for (int i = 1; i <= n; ++i) {
if (a[i] - 1) l[i] = ask(a[i] - 1);
add(a[i], 1);
}
memset(bit, 0, sizeof(bit));
for (int i = n; i >= 1; --i) {
if (a[i] + 1 <= n) r[i] = ask(tot) - ask(a[i]);
add(a[i], 1);
}
ll ans = 0;
for (int i = 1; i <= n; ++i) ans += (ll)l[i] * r[i];
cout << ans << endl;
return 0;
}

思路

假设离散化后,1是最小数字的排名,n是最大数字的排名,把原来的数字用排名替代。

遍历时使用权值树状数组记录每个排名的出现次数,

从左向右遍历,对于每个当前排名ai,比它小的排名区间为1到ai - 1,那么比它小的个数就等于ask(a[i] - 1)

结果保存到l[i]

从右往左遍历,对于每个当前排名ai,比它大的排名区间为ai + 1 到 n,那么比它大的个数就等于ask(n) - ask(a[i])

结果保存到r[i]

根据乘法原理,答案就等于l[i] * r[i]之和