题目大意:
题目链接: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
]
<
a
[
x
]
+
b
[
2
]
<
.
.
.
<
a
[
x
]
+
a
[
n
]
(
x
∈
[
1
,
n
]
)
a[x]+b[1]<a[x]+b[2]<...<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;
}