【洛谷P1631】序列合并【堆】

题目大意:

题目链接:https://www.luogu.org/problemnew/show/P1631
有两个长度都是 n n n的序列 a a a b b b,在 a a a b b b中各取一个数相加可以得到 n 2 n^2 n2个和,求这 n 2 n^2 n2个和中最小的 n n n个。


题外话:

AC500祭 \color{red}\texttt{AC500祭} AC500在这里插入图片描述

我的正确率是真的低qwq


思路:

由于题目中说 a , b a,b a,b都是单调递增的。所以就可以不考虑排序。
单调递增同时特满足了
a [ x ] + b [ 1 ] &lt; a [ x ] + b [ 2 ] &lt; . . . &lt; a [ x ] + a [ n ] ( x ∈ [ 1 , n ] ) a[x]+b[1]&lt;a[x]+b[2]&lt;...&lt;a[x]+a[n](x\in [1,n]) a[x]+b[1]<a[x]+b[2]<...<a[x]+a[n](x[1,n])

所以我们每次只要维护 n n n个数的最小值就可以了(分别是 a [ 1 ] + b [ k 1 ] , a [ 2 ] + b [ k 2 ] , . . . , a [ n ] + b [ k n ] a[1]+b[k_1],a[2]+b[k_2],...,a[n]+b[k_n] a[1]+b[k1],a[2]+b[k2],...,a[n]+b[kn],其中 k i k_i ki表示 i i i所维护完的 b b b数组指针)
求最小值可以考虑维护一个小根堆。每一个元素维护一个三元组 ( v a l , x , k ) (val,x,k) (val,x,k),表示这个 a [ x ] + b [ k ] a[x]+b[k] a[x]+b[k]的值。
如果 ( v a l i , x i , k i ) (val_i,x_i,k_i) (vali,xi,ki)是堆里的最小值,那么就输出 v a l i val_i vali,弹出,并插入 ( a [ x i ] + b [ k i + 1 ] , x i , k i + 1 ) (a[x_i]+b[k_i+1],x_i,k_i+1) (a[xi]+b[ki+1],xi,ki+1)
n n n次上述方法即可。
时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)


代码:

#include <queue>
#include <cstdio>
#define mp make_pair
using namespace std;

const int N=100010;
int n,a[N],b[N];
priority_queue<pair<int,pair<int,int> > > q;  //pair套pair最为致命(三元组)

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for (int i=1;i<=n;i++)
		scanf("%d",&b[i]);
	for (int i=1;i<=n;i++)
		q.push(mp(-a[i]-b[1],mp(i,1)));  //插入初始值
	for (int i=1;i<=n;i++)
	{
		printf("%d ",-q.top().first);
		int x=q.top().second.first,y=q.top().second.second;
		q.pop();
		q.push(mp(-a[x]-b[y+1],mp(x,y+1)));
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值