离散化后用两次 Fenwick 扫描统计每个中间位置左侧更小和右侧更大的数量。
OJ: luogu
题目 ID: P1637
难度:普及+/提高
标签:树状数组离散化计数python
日期: 2026-07-16 23:59
题意
统计 i < j < k 且 a[i] < a[j] < a[k] 的三元组数量。
思路
固定中间位置 j。左侧比 a[j] 小的个数乘右侧比它大的个数,就是以 j 为中间点的方案数。离散化后,正向 Fenwick 查询严格更小的排名,反向 Fenwick 查询严格更大的排名,最后求乘积之和。
Python 知识
sorted(set(values))加字典推导式完成离散化。- 反向扫描时用
seen - prefix(rank)处理严格大于,避免重复值被计入。 zip(left_count, right_count)直接组合两个计数列表。
代码
python
import sys
data = list(map(int, sys.stdin.buffer.read().split()))
n, values = data[0], data[1:]
rank = {value: index for index, value in enumerate(sorted(set(values)), 1)}
size = len(rank)
def add(tree, index):
while index <= size:
tree[index] += 1
index += index & -index
def query(tree, index):
result = 0
while index:
result += tree[index]
index -= index & -index
return result
left_count = []
tree = [0] * (size + 1)
for value in values:
position = rank[value]
left_count.append(query(tree, position - 1))
add(tree, position)
right_count = [0] * n
tree = [0] * (size + 1)
seen = 0
for index in range(n - 1, -1, -1):
position = rank[values[index]]
right_count[index] = seen - query(tree, position)
add(tree, position)
seen += 1
print(sum(left * right for left, right in zip(left_count, right_count)))原有 C++ 版本仍保留:
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-16 23:46
* update_at: 2026-07-16 23:46
*/
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
return 0;
}复杂度
离散化 O(n log n),两次 Fenwick 扫描 O(n log n),空间 O(n)。
总结
三元组计数的关键是固定中间点,把三维条件拆成两个一维前缀统计。