P1631 序列合并

题目描述

有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到N^2个和,求这N^2个和中最小的N个。

输入输出格式

输入格式:

第一行一个正整数N;

第二行N个整数Ai, 满足AiAi+1Ai10^9;

第三行N个整数Bi, 满足BiBi+1Bi10^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(1in)

合在一起,共1/1+1/2+...+1/n

欧拉证明过,上面的无穷级数的增长率为O(lnn)的

因此,总复杂度为O(nlognlnn)也就是O(nlog2n)的,证毕

原文地址:https://www.cnblogs.com/-hhs/p/11005771.html