P1631 序列合并
题目描述
有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到N^2个和,求这N^2个和中最小的N个。
输入输出格式
输入格式:
第一行一个正整数N;
第二行N个整数Ai, 满足Ai≤Ai+1且Ai≤10^9;
第三行N个整数Bi, 满足Bi≤Bi+1且Bi≤10^9.
【数据规模】
对于50%的数据中,满足1<=N<=1000;
对于100%的数据中,满足1<=N<=100000。
输出格式:
输出仅一行,包含N个整数,从小到大输出这N个最小的和,相邻数字之间用空格隔开。
输入样例1:
3 2 6 6 1 4 8
输出样例1:
3 6 7
思路:
大根堆处理,如果对于每一个二元组,我们都压入堆中,复杂度将是O(n2logn),会T掉,所以我们对于加入操作进行优化。
考虑到如果a[i]+b[i]对于堆没有贡献,那么a[i]+b[i+1]也一定不会对堆有贡献,所以直接break掉就好。复杂度降为O(nlog2n),最后有证明。
代码:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #define maxn 100008 using namespace std; int n,a[maxn],b[maxn],ans[maxn]; priority_queue <int> q; long long read() { long long x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int main() { n=read(); for(int i=1;i<=n;++i) a[i]=read(); for(int i=1;i<=n;++i) b[i]=read(); for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) { int x=a[i]+b[j]; if(q.size()<n) q.push(x); else if(q.top()>x) q.pop(),q.push(x); else break; } for(int i=1;i<=n;++i) { ans[i]=q.top(); q.pop(); } for(int i=n;i>=1;--i) printf("%d ",ans[i]); return 0; }
复杂度证明:
第一行至多扫完它的1/1
第二行变为1/2
以此类推,第i行至多1/i(1≤i≤n)
合在一起,共1/1+1/2+...+1/n
欧拉证明过,上面的无穷级数的增长率为O(lnn)的
因此,总复杂度为O(nlognlnn)也就是O(nlog2n)的,证毕–