[GZOI2017] 配对统计

GitHub跳转原题关系图返回列表

按值排序找每个位置的最近值邻居,把好配对转成二维点并离线 Fenwick 查询。

OJ: luogu

题目 ID: P5677

难度:省选/NOI-

标签:离线查询树状数组最近邻python

日期: 2026-07-16 21:00

题意

有序对 (x,y)a[y]a[x] 的全局最近值时为好配对。询问下标区间内包含多少好配对,并按询问编号加权。

思路

数值互异。按值排序后,某个数的最近值只可能是相邻的前驱或后继;距离相等时两者都算。因此有序好配对总数至多 2n

把每个有序对变成点 (max(x,y), min(x,y))。查询 [l,r] 要求第一坐标不超过 r 且第二坐标不小于 l。按查询右端排序,逐步把第一坐标合格的点加入 Fenwick;已加入总数减去第二坐标小于 l 的前缀数就是答案。

Python 知识

  • sorted(range(n), key=values.__getitem__) 得到按值排列的原下标。
  • key=lambda query: query[1] 明确按右端点离线排序。
  • 重复二维点不能去重,因为相反方向是两组有序配对。

代码

python
import sys


data = iter(map(int, sys.stdin.buffer.read().split()))
n, query_count = next(data), next(data)
values = [next(data) for _ in range(n)]
ordered = sorted(range(n), key=values.__getitem__)
pairs = []

for position, index in enumerate(ordered):
    left_gap = values[index] - values[ordered[position - 1]] if position else 10**30
    right_gap = values[ordered[position + 1]] - values[index] if position + 1 < n else 10**30
    if left_gap <= right_gap:
        other = ordered[position - 1]
        pairs.append((max(index, other), min(index, other)))
    if right_gap <= left_gap:
        other = ordered[position + 1]
        pairs.append((max(index, other), min(index, other)))

queries = sorted(((next(data) - 1, next(data) - 1, identity + 1)
                  for identity in range(query_count)), key=lambda query: query[1])
pairs.sort()
tree = [0] * (n + 1)


def add(index):
    index += 1
    while index <= n:
        tree[index] += 1
        index += index & -index


def prefix(end):
    result = 0
    while end:
        result += tree[end]
        end -= end & -end
    return result


pointer = 0
answer = 0
for left, right, identity in queries:
    while pointer < len(pairs) and pairs[pointer][0] <= right:
        add(pairs[pointer][1])
        pointer += 1
    answer += (pointer - prefix(left)) * identity
print(answer)

复杂度

排序 O((n+m)logn)O((n+m)\log n),Fenwick 操作 O((n+m)logn)O((n+m)\log n),空间 O(n+m)O(n+m)

总结

先证明好配对稀疏,再把“两个下标都在区间”转成二维偏序,是本题关键。