舞蹈课

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

堆选技术差最小异性邻对,前驱后继数组维护删除后的队列相邻关系。

OJ: luogu

题目 ID: P1878

难度:提高+/省选-

标签:二叉堆链表懒删除python

日期: 2026-07-16 21:00

题意

反复让技术差最小、平局最靠左的相邻异性出列,输出配对顺序。

思路

堆保存所有当前或历史候选 (差值,左编号,右编号),元组顺序自动处理平局。前驱、后继数组模拟双向链表;删除一对后只可能新产生跨过这两人的一个邻对。

堆中旧候选不主动删除,弹出时检查两人是否仍存活且仍相邻,不合法就跳过。

Python 知识

  • bytearray 紧凑保存存活标志。
  • 两个整数列表实现节点固定编号的双向链表。
  • heapq 元组第二字段为左编号,天然满足最左优先。

代码

python
import heapq
import sys


input = sys.stdin.buffer.readline
n = int(input())
gender = input().strip()
skill = list(map(int, input().split()))
previous = [i - 1 for i in range(n)]
following = [i + 1 for i in range(n)]
following[-1] = -1
alive = bytearray(b"\1" * n)
heap = [(abs(skill[i] - skill[i + 1]), i, i + 1)
        for i in range(n - 1) if gender[i] != gender[i + 1]]
heapq.heapify(heap)
answer = []

while heap:
    _, left, right = heapq.heappop(heap)
    if not alive[left] or not alive[right] or following[left] != right:
        continue
    answer.append((left + 1, right + 1))
    alive[left] = alive[right] = 0
    new_left, new_right = previous[left], following[right]
    if new_left >= 0:
        following[new_left] = new_right
    if new_right >= 0:
        previous[new_right] = new_left
    if new_left >= 0 and new_right >= 0 and gender[new_left] != gender[new_right]:
        heapq.heappush(heap, (abs(skill[new_left] - skill[new_right]), new_left, new_right))

print(len(answer))
print("\n".join(f"{left} {right}" for left, right in answer))

复杂度

每人删除一次,每个新邻对入堆一次,时间 O(nlogn)O(n\log n),空间 O(n)O(n)

总结

“动态相邻 + 全局最优”常用链表维护局部变化、堆维护全局候选,再以懒删除连接两者。