P1631 序列合并

把二维和表看成若干条有序序列

问题

P1631 序列合并
有两个长度均为 nn 的单调不降序列 A,BA,B。从两个序列中各选一个数相加,一共能得到 n2n^2 个和,需要按从小到大的顺序输出其中最小的 nn 个。不能显式生成所有和,否则时间与空间都会达到 O(n2)O(n^2)。

数据规模:1≤n≤1051\le n\le10^5,1≤Ai,Bi≤1091\le A_i,B_i\le10^9。

我最开始觉得它像双指针,因为每次输出一个和以后,确实只有某个下标需要向后移动。不过普通双指针只比较两个候选,这里同时有 nn 行候选,可以扩展到 nn 指针。

固定 ii 后,Ai+B1,Ai+B2,…,Ai+BnA_i+B_1,A_i+B_2,\dots,A_i+B_n 本身是一条有序序列。

所以原问题可以转化为,我在这 nn 条序列中,挑选出 nn 个最小的数,也就是所谓的 “多路合并”。

先把每一条序列的首项 (Ai+B1,i,1)(A_i+B_1,i,1) 放入小根堆。每次弹出当前最小值后,若它来自第 ii 条序列的第 jj 项,就把同一条序列的下一项 (Ai+Bj+1,i,j+1)(A_i+B_{j+1},i,j+1) 加入堆中;弹出 nn 次便得到答案。

堆中始终只保留每条序列尚未输出部分的最小元素,因此堆顶就是所有未输出元素中的最小值。时间复杂度为 O(nlog⁡n)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
ll a[maxn], b[maxn];

struct Node {
ll val;
int i, j;
bool operator < ( const Node &oth ) const {
return val > oth.val;
}
};

void solve( ) {
int n = read( );
for( int i = 1; i <= n; i ++ ) a[i] = read( );
for( int i = 1; i <= n; i ++ ) b[i] = read( );

priority_queue<Node> pq;
for( int i = 1; i <= n; ++ i ) {
pq.push({ a[i] + b[1], i, 1 });
}

for( int k = 1; k <= n; ++ k ) {
auto [val, i, j] = pq.top( );
pq.pop( );
cout << val << " ";
if( j < n ) {
pq.push({ a[i] + b[j + 1], i, j + 1 });
}
}
}

P1631 序列合并
http://miqiu.space/posts/p1631001/
作者
Miqiu
发布于
2026年8月17日
许可协议